Contents > Native Add-ins

Native Add-ins

| Add-in Overview | Overview and usage of native add-ins.
| Creating Add-ins | Creating native add-ins.
| VM Architecture | OrbC virtual machine architecture.
| Native Structs | Implementing native structs.
| Native Gadgets | Implementing native gadgets.
| System Events | Intercepting system events.
| Type Strings | Processing type string.
| Add-in Reference | OrbC native interface reference.

Contents > Native Add-ins > Add-in Overview

Add-in Overview

Native add-ins provide developers with the ability to extend the functionality of OrbC, and to write performance critical functionality in native code. A native add-in is a code resource in the form of a .prc file. When an application that uses a native add-in is built standalone, the add-in is also embedded into the application. This way, a standalone application is completely self-contained, even when using native add-ins. A native add-in can also contain additional resources, such as forms or bitmaps. These will also be embedded in the application.

### Using Native Add-ins

In order to use the functions and structs implemented in a native library, you must include the add-in's declaration file. To do this, add a line to the beginning of you primary source file such as this:

`#include "addin.oc"`

This declaration file may be in the project directory, or you must supply a relative path from the current project OR from the compiler root. For example, to include the OrbSort native library located in the compiler's "add-ins" directory:

`#include "add-ins/OrbSort.oc"`

### Using Native Gadgets

Using a native gadget is the same as using any other gadget. Simply add the gadget declaration file to the project and create a gadget in your form.

### Locating the Native Add-in's .PRC File

When building a standalone application, the compiler will look for the native add-in's .prc file so that its contents can be embedded in the application. To find this, the compiler searches in this order:

- The project directory
- The "add-ins" directory under the OrbForms or PocketC Architect installation directory (usually something like: C:\Program Files\OrbForms\add-ins or C:\Program Files\PocketC Architect\add-ins)

Contents > Native Add-ins > Creating Add-ins

Creating Add-ins

A native add-in is implemented as a code resource. As a code resource, the add-in only has one exported function (as opposed to a system library, which can have numerous entries). The exported function, the entry function, is called to startup, shutdown, and execute code in the add-in.

This documentation is not a step-by-step tutorial for creating a native add-in. The easiest way to create a native add-in is to copy one of the sample add-ins, modifying it for your needs.

### Entry Function

The entry function, which must be named __Startup__ to satisfy the linker, takes 3 parameters:

`void* __Startup__(OrbFormsInterface* ofi, void* data, UInt16 iFunc)`

The first parameter is a structure embodying the OrbC interface which contains a number of functions to access the virtual machine and other functionality. The second parameter, data, is a pointer to the add-in's global data. The final parameter is the function number that is to be executed.

### Startup and Shutdown

Two special function indexes are defined for startup and shutdown of the add-in - NATIVE_STARTUP and NATIVE_SHUTDOWN.

- NATIVE_STARTUP - this is your opportunity to allocate and initialize any global data needed. A pointer to the allocated data (or NULL, if not needed) should be returned from the entry function. All calls after this will provide this data pointer as a parameter.
- NATIVE_SHUTDOWN - this is used to cleanup any state or data allocated. At this point, the global data must be cleaned up if it was allocated.

### Project Settings

In order for a native add-in to be compatible with OrbC, it must follow certain guidelines.

- The code resource should be type 'ofNA', with id '0'
- If you use string literals, you must specify that these strings are PC relative
- Creator ID should be registered with Palm.

### Additional Resources

If your native add-in requires additional resources, such as forms or bitmaps, you can add these to the .prc file. When a standalone application is built, these additional resources will be copied over.

### OrbC Declarations

In order to use a native function in OrbC, you must declare it as specified below. You should create a .oc file with these declarations and include it in the main source file of your application. If you are creating a native gadget, you should add the .oc file with the declarations to the project as a gadget declaration file.

A native function is declared like a normal function, with additional syntax that specifies the name of the add-in (without the .prc) and the function index that implements it:

    int add(int x, int y) @ library("AddInName", 1);
    string find(string) @ library("AddInName", 2);
    struct MyStruct {
      void doSomething(float) @ library("AddInName", 3);
    };

A native struct can expose properties, which can be readable, writeable, or both. A property is exposed as a function call and has an index associated with the getting and setting of it, as shown below.

    struct MyStruct {
      // 4 is get, 5 is set
      int read_write_property @ library("AddInName", 4:5);
      string read_only_property @ library("AddInName", 6);
      char write_only_property @ library("AddInName", :7);
    };

Contents > Native Add-ins > VM Architecture

VM Architecture

The OrbC runtime is built around a stack-based virtual machine. The virtual machine's memory is represented by Values, as defined in OrbNative.h. The following text describes the architectural aspects that are important to a native add-in.

### Values

A Value is a union of the supported basic data types - int, float, string, and char. ints, floats, and chars are stored directly in the Value structure. Strings, however, are stored as ref-counted handles.

Other types supported by the language, such as pointers and bools, are treated as ints by the virtual machine.

### Strings

There are two types of strings supported by the virtual machine: constant and shared. A constant string is just a pointer to some constant data, usually a string literal in C++ code. A shared string is a handle to a ref-counted structure containing the string's data. This structure is not exposed directly to the native add-ins. Instead, functions are provided to create and modify these strings.

### Function calls

In order to implement a function in a native add-in, your code must pop all the arguments off the stack, do something useful, and then (optionally) set the return value. If you declare your function with a return type, you MUST set a return value.

The calling convention for functions is quite simple. Each argument is pushed onto the stack from left to right, which means that the last argument will be on top of the stack. For example, an add function declared as "`int add(int x, int y)`" looks like this:

    void add(OrbFormsInterface* ofi) {
      Value* y = ofi->vm_pop(); // pop the last arg
      Value* x = ofi->vm_pop(); // pop the first arg
      ofi->vm_retVal->type = vtInt; // set the return type
      ofi->vm_retVal->iVal = x->iVal + y->iVal;
    }

Note: If your function takes an object (or structure) as a parameter, this is converted to a pointer by the compiler. So, a function declared as "`void box(Point top_left, Point bottom_right)`" will be implemented as if it were "`void box(Point* top_left, Point* bottom_right)`".

### Method calls

A method call is the same as function call, but the compiler inserts a parameter to the beginning of the parameter list which is a pointer to the object. For example, "`void Point.add(int x, int y)`" will be implemented as "`void Point_add(Point* this, int x, int y)"`.

### Returning objects

If a function returns an object (or structure), the compiler adds a pointer to the return location as the first parameter. For a method that returns an object (or structure), the "this" pointer gets moved to the second parameter. In addition to filling in the structure passed as the first parameter, a function or method must also set the virtual machine's return value to type point to this same structure.

    struct Point { int x, int y; };
    Point makePt(int x, int y) @ library("AddInName", 0);

    void makePt(OrbFormsInterface* ofi) {
      Value* y = ofi->vm_pop();
      Value* x = ofi->vm_pop();
      Value* optr = ofi->vm_pop(); // pointer to return struct
      Value* o_x = ofi->vm_deref(optr->iVal); // dereference the struct pointer to
      Value* o_y = ofi->vm_deref(optr->iVal + 1); // get pointers to the two fields
      // fill in the return struct
      o_x->iVal = x->iVal;
      o_y->iVal = y->iVal;
      // return the pointer as well!
      ofi->vm_retVal->type = vtInt;
      ofi->vm_retVal->iVal = optr->iVal;
    }

### Properties

A property is similar to a field in an object (or structure) - except that it is not actually stored in the object's memory. Instead, the value of the property is get and set by calling a native function. A property may only be a simple type, not an object or structure.

Getting and setting a property is implemented as a method call. A "get" method takes a pointer to the object and returns the property value. A "set" method takes a pointer to the object and the new property value, returning a copy of the new property value. For example, if you implemented `Point` as a native struct with `x` as a read-write property:

    struct Point { int x @ library("AddInName", 0:1); };

the methods that get and set the properties would be implemented like this:

    int Point_get_x(Point* this)
    int Point_set_x(Point* this, int new_x)

### Object/Struct Memory Layout

All the fields in an object and structure are laid out in memory in the same order as they are declared. Methods and properties require no space in the struct. For example:

    struct Point {
      int x; // location 0
      int y; // location 1
    };
    struct NativeStruct {
      int id; // location 0
      void method(); // no space
      string prop @ library("AddInName", 1:2); // no space
      int state; // location 1
      Point pt; // locations 2 and 3
    };

The memory layout for an object is slightly different than a struct. The first memory location in an object is an object type id used by the runtime engine. For example:

    object Point {
      // location 0 is object type id
      int x; // location 1
      int y; // location 2
      void setPoint(int x, int y); // no space
    };

Contents > Native Add-ins > Native Structs

Native Structs

A native struct can be implemented in many ways. The documentation below explains the model that the OrbC native interface supports through its helper functions. In addition to the text below, it would be informative to read through the sample add-ins. The SampleAddIn (samples\Add-ins\SampleAddIn) implements SampleSerial, a native struct that demonstrates accessing the serial port.

Note: Currently native objects are not supported. What were previously called native objects are actually native structs. All the previous features are still supported, but with the current version of the compiler a struct must be used (because inheritance and virtual methods are not supported).

### Native Struct IDs

Each native struct in the application has a unique id. This id is stored in an internal table which maps it to a native struct pointer. In the OrbC declaration, the struct defines only one field: this id - all other state and data of the struct are retrieved and set through properties and methods. To make coding easy, the OrbC native interface provides a function (`nobj_pop`) to pop the struct pointer from the stack, dereference it to retrieve the id, and map the id to the native struct pointer.

### Reference Counting

Native structs are reference counted by the runtime environment. When a struct is created or copied, its reference count is increased automatically. When a struct goes out of scope or is deleted, the reference count is decremented. When the reference count reaches zero, the native struct's delete function is called. To support this reference counting mechanism, your native struct must declare a `_copy` method and a `_destroy` method that call the built in functions (80 and 89) as specified below:

    struct Native {
      int id;
      void _init() @ library("AddInName", 0);
      void _destroy() @ 80;
      void _copy(Native) @ 89;
    }

In addition to the reference counting handled by the runtime, you may add explicit references to a struct. A common reason to do this is to ensure that the lifetime of a container struct is greater than the lifetime of a struct it owns. For example, a database owns the database records, so the database struct must live longer than the record. To ensure this, the database record struct adds an explicit reference to the database struct when it is created, and removes the reference when it is destroyed.

Another advantage to using the ref-counted native structs is that the runtime environment will be able to clean up all resources that the application neglected, even if the application is stopping due to an error.

### Creating a Native Struct

To create a native struct, you must allocate the state needed by the struct and call the supplied OrbC native interface function `nobj_create` from your struct's `_init` method. This function pops the struct's pointer from the stack, so you must NOT do this before hand. As a parameter to `nobj_create`, you must specify the native struct's delete function. This function will be called automatically by the runtime when the struct's reference count reaches 0.

Contents > Native Add-ins > Native Gadgets

Native Gadgets

Native gadgets are an easy way to create rich, responsive user interfaces. The following documentation explains the rules for creating them, but reading the sample code will be extremely useful. The SampleAddIn (samples\Add-ins\SampleAddIn) implements a native gadget called the TargetGadget. The NativeGadget sample uses this gadget.

### Native Gadget IDs

Gadgets are implemented as structs in OrbC, so the methods of a gadget have the struct pointer as the first parameter. Similar to native structs, a gadget implemented natively has a first field in OrbC memory which is its id. Unlike a native struct, this id is based on the resource id of the Palm OS control.

### Associated Data

Each instance of a native gadget can have native data associated with it. This data should be allocated in the onopen handler and set using the OrbC native interface function `gadget_set_data`. For other method calls, this data can be retrieved by popping the struct pointer, dereferencing it to retrieve the id, and calling `gadget_get_data`. The data should be freed in the onclose handler, and the data pointer should be set to null.

### Drawing and Bounds

When drawing in a gadget, the native code must honor the gadget's bounds which are relative to the containing form. When a gadget's handler is called, the active drawing window will be the form containing the gadget. In order to draw in the gadget, you must add it's x and y coordinates to the points you plan to draw (see the sample for an example). Optionally, you may want to set a clipping rectangle to the location and size of the gadget to prevent accidentally drawing on the form.

### Retrieving Event Data

When the gadget's native handler is called, you may need to retrieve the event data (such as event.x in OrbC code). To do this, call the Event struct's property method (find the index in orbc.sys). See the sample code for an example.

### Calling Custom Event Handlers

Custom event handlers are stored as code pointers in the gadget struct. The handler is stored as an int which is 0 if the custom event is not handled by the instance, or the address of the method if it is handled. If implemented, the handler should be called by pushing the pointer to the gadget onto the stack and using `vm_call` to call the method.

The location of the event handler's address is determined by the location of the handler declaration. For example, if the gadget's first member is a UIGadget (which is must be), and the second member is "`handler customEvent`", then the offset of the handler is 1. For example:

    struct NativeGadget {
      // the UIGadget is required to be the first field.
      // it contains one member, which is the resource id
      // of the gadget.
      UIGadget gadget; // location 0
      void someMethod(); // takes no space in the struct
      handler onCustomEvent; // location 1
      int state; // location 2;
    };

Contents > Native Add-ins > System Events

System Events

A native add-in can intercept system events by registering an event function with OrbC using the `reg_event_func` function of the OrbC native interface. To see an example, see the `hookcontrast` function implemented in SampleAddIn.

Event handling in the Palm OS is normally implemented in a loop like this:

    do {
      EvtGetEvent(&event, timeout);

      if (! SysHandleEvent(&event))
        if (! MenuHandleEvent(0, &event, &error))
          if (! AppHandleEvent(&event))
            FrmDispatchEvent(&event);

    } while (event.eType != appStopEvent);

This is very similar to the way events are handled in the OrbC runtime environment. When a native add-in registers an event function, it is called before SysHandleEvent. If the add-in's function handles the event, it must return true to prevent the other event functions from processing the event. When more than one native add-in registers an event function, there are no guarantees about which add-in will get the first chance to process the event.

Contents > Native Add-ins > Type Strings

Type Strings

Many types of native functions and methods have an interest in serializing and de-serializing user data, such as the way many built-in methods do. For example, both DBRecord.write and Preferences.set take a pointer to arbitrary user data and serialize the data according to a user specified type string. To help native functions and methods process these type strings, the OrbC native interface supplies a few helpful functions. See the OrbSerial sample to see these functions in action.

### Memory Iteration

The type string functions operate by iterating over a block of memory where each Value in the memory block is processed according to the type string. If the data in the block of memory is not compatible with the type string, a runtime error will be raised. When using the `ts_iterate` function, you provide a `NATIVE_TS_ITER` callback function. This callback function is called once for each Value in memory. Along with a pointer to the Value in memory, this callback function is also provided with the type and size as defined in the type string. The following table maps the type string values to the resulting function parameters:

`typedef bool (*NATIVE_TS_ITER)(OrbFormsInterface*, Value* val, VarType type, int size, void* context)`

Specifier `strFormat` | Primitive Type `type` | Data Size `size` | Valid Memory Type(s) `val->type`
--- | --- | --- | ---
i | int | 4 | vtInt
w | 2-byte int | 2 | vtInt
b | 1-byte int | 1 | vtInt or vtChar
c | char | 1 | vtInt or vtChar
f | float | 4 | vtFloat
s | string | 0 | vtString
l | fixed-length string | len | vtString
o | object type id | -- | --

When an object type id is encountered it is skipped rather than being passed to the native add-in.

Contents > Native Add-ins > Add-in Reference

Add-in Reference

The OrbC native interface is exposed through a structure of function pointers which is passed to the add-in's entry function. The structure is defined in OrbNative.h (in the add-in directory) as follows:

    struct OrbFormsInterface {
      UInt16 version;
      // VM interface
      Value* (*vm_pop)();
      void   (*vm_push)(Value*);
      Value* vm_retVal;
      void   (*vm_error)(char*);
      Value* (*vm_deref)(long ptr);
      void   (*vm_call)(long ptr);
      void   (*vm_callBI)(long index);

      // Value interface
      void  (*val_release)(Value*);
      void  (*val_acquire)(Value*, Value*);
      char* (*val_lock)(Value*);
      void  (*val_unlock)(Value*);
      void  (*val_unlockRelease)(Value*);
      char* (*val_newString)(Value*, int len);
      char* (*val_newConstString)(Value*, const char*);
      bool  (*val_copyString)(Value*, const char*);

      // Native struct interface
      void* (*nobj_pop)();
      void* (*nobj_get_data)(long id);
      long  (*nobj_create)(void* obj, NOBJ_DELETE_FUNC delfunc);
      long  (*nobj_addref)(long id);
      long  (*nobj_delref)(long id);

      // Gadget interface
      void* (*gadget_get_data)(long id);
      void  (*gadget_set_data)(long id, void*);
      void  (*gadget_get_bounds)(long id, int* pX, int* pY, int* pW, int* pH);

      // System event interface
      bool  (*event_reg_func)(NATIVE_EVENT_FUNC eventfunc);
      bool  (*event_unreg_func)(NATIVE_EVENT_FUNC eventfunc);

      // Type string processing
      long  (*ts_iterate)(long addr, char* strFormat, int count,
          NATIVE_TS_ITER func, void* context);
      long  (*ts_data_size)(long addr, char* strFormat, int count);
    };

### Virtual Machine

- `UInt16 version` - the version of the runtime environment that has loaded the add-in. The top byte is the major version, the bottom byte is the minor and bug fix version. For example, version 1.1.0 is 0x0110
- `Value* vm_pop()` - retrieves the value from the top of the stack. If this is value is a string, you must release it when finished. Note: The value returned actually points into the stack memory (instead of a copy). Therefore, any function which writes to the stack will overwrite it. Also, calling any Palm OS function that could cause an OrbC handler to run can possibly overwrite the value.
- `void vm_push(Value* val)` - copies the value to the top of the stack. If this is a string, the ownership is passed to the stack, so you should not release it.
- `Value* vm_retVal` - the return value register. If your function is declared to return a value, you must set the type and data of the value. If you function does not return a value, you must not set it.
- `void vm_error(char* text)` - displays the specified text as an error message, and immediately terminates the application.
- `Value* vm_deref(long ptr)` - retrieves a pointer to the value stored at location ptr (either in the OrbC stack or heap). It is not safe to assume that two contiguous values in OrbC memory are contiguous in actual memory, so you must call this to retrieve every value. i.e. in: "`Value* v = ofi->vm_deref(ptr); v++;`" v++ makes is invalid.
- `void vm_call(long ptr)` - call an OrbC function, method, or gadget custom event handler. This method will modify the stack and modify `vm_retVal`. If the function you call returns a value, you must pop it from the stack.
- `void vm_callBI(long index)` - call a built in function, method, or property. `index` is the index of the function, which can be found in the orbc.sys file. This method will modify the stack and modify `vm_retVal`. If the function you call returns a value, you must pop it from the stack.

### Values

All of the Value functions operate on string values. If a Value already contains a string, it must be released before assigning any new value. If your function modifies a string, you must make sure that it is a shared string with no other Value referencing it. To do this, first release the string, then allocate a new string to store the modified value.

- `void val_release(Value* val)` - release the string. If this is a shared string, the reference count is decremented, and the string is possibly freed.
- `void val_acquire(Value* val, Value* src)` - copy the source string into this Value. If this is a shared string, the reference count is increased.
- `char* val_lock(Value* val)` - lock the string and return a pointer to the data. Each call to `val_lock` must be matched by a call to `val_unlock`.
- `void val_unlock(Value* val)` - unlock the string.
- `void val_unlockRelease(Value* val)` - unlock and release the string. This does the same thing as calling `val_unlock` followed by `val_release`, but is more efficient.
- `char* val_newString(Value* val, int len)` - allocates a new string of length `len`. The length does not include the null terminator, so `val_newString(val, 5)` allocates enough room to store "apple". Upon return the string is locked, so you must call `val_unlock` after you have filled the buffer. Returns null if unable to allocate.
- `char* val_newConstString(Value* val, const char* str)` - sets the value to point to the given constant string, returning the same pointer passed in. The pointer must either be a literal string, or must remain valid until the application exits. Upon return, the value is already unlocked.
- `bool val_copyString(Value* val, const char* str)` - Creates a new shared string by copying the supplied string. Upon return the value is already unlocked. Returns false if unable to allocate.

### Native Struct Functions

- `void* nobj_pop()` - pops the pointer to the struct off the stack, dereferences it to retrieve the id, and returns the associated native struct pointer.
- `void* nobj_get_data(long id)` - retrieves the native struct pointer associated with the given id.
- `long nobj_create(void* obj, NOBJ_DELETE_FUNC delfunc)` - create a new native struct. This should be called from the struct's _init method. This function pops the struct pointer off the stack (so your function must not), allocates a new id for it, and stores the id into the struct's id field. The native struct pointer (`obj`) is associated with this id. When the native struct's reference count goes to 0, the supplied delete function is called. The struct's assigned id is returned.
- `long nobj_addref(long id)` - Add an explicit reference to the struct with the given id, returning the new reference count.
- `long nobj_delref(long id)` - Delete an explicit reference from the struct with the given id, returning the new reference count. If the count reaches 0, the struct's delete function will be called before this function returns.

### Gadget Functions

- `void* gadget_get_data(long id)` - retrieves the data associated with the given gadget id.
- `void gadget_set_data(long id, void*)` - associates the given data with the gadget id.
- `void gadget_get_bounds(long id, int* pX, int* pY, int* pW, int* pH)` - retrieves the bounds of the given gadget.

### System Event Functions

- `bool event_reg_func(NATIVE_EVENT_FUNC eventfunc)` - registers the given event function to be called on each system event that occurs. Returns true if the function was able to be registered.
- `bool event_unreg_func(NATIVE_EVENT_FUNC eventfunc)` - unregisters the given event function.

### Type String Functions

- `long ts_iterate(long addr, char* strFormat, int count, NATIVE_TS_ITER func, void* context)` - iterates over a block of memory starting at address `addr`, according to the format specified by `strFormat` (repeated `count` times). Returns the number of values processed. For each value in memory, the function `func` is called as described below. The `context` parameter is passed to the callback function. See OrbSerial sample for usage example.
- `typedef bool (*NATIVE_TS_ITER)(OrbFormsInterface*, Value* val, VarType type, int size, void* context)` - user supplied function called for each value processed by `ts_iterate`. For each value, a pointer to the Value is provided, along with the type and size specified by the format string. See Native Type Strings for a table of possible parameter values. Return true to continue the iteration, or false to force it to end prematurely.
- `long ts_data_size(long addr, char* strFormat, int count)` - calculates the total size required to serialize a the block of memory starting at address `addr`, based on the format string `strFormat` (repeated `count` times)