The Palm Interface Definition Generator (pidgen) is a tool that makes it easier to generate Binder interfaces. Pidgen parses interface files written in Interface Definition Language (IDL) and generates the necessary code for objects declared in an interface to travel across processes. Pidgen does not write your code for you; you still have to write the actual implementation (that is, you have to write the BClass).
Pidgen parses IDL, which is similar to COM and CORBA's IDL. IDL shares syntactical similarities with C++, but IDL is not the same as C++. The marshaling and unmarshaling code that pidgen generates is C++. Note that pidgen does not parse C++, it simply generates C++.
Here is what is automatically generated when pidgen parses an IDL file:
The concept of an interface is central to IDL. An interface file is a set of definitions that are abstract representations of the underlying system. Developers can use these definitions to safely send objects declared in the interface across processes, without having to know how the underlying system is implemented. It is the abstract representation of the parts of your system’s underlying implementation that is open to modification by third-party developers.
The purpose of using pidgen is to produce safe, proven code that enables objects defined in an interface to be used across processes.
To specify an interface, declare it in an IDL file as follows:
interface xyz
You can declare multiple interfaces in one IDL file, but the convention is to declare at least interface IXyz in the IDL file named IXyz.idl. For example, you would declare interface IWidget in IWidget.idl.
Usually, declarations of interfaces are enclosed in namespaces to help pidgen map its C++ code output to the correct locations. For example, an interface that lives in the app kit is declared like this:
namespace palmos { namespace widget { interface IWidget { ... } }}
By convention, interfaces names begin with the letter I; for example, IRender or ICommand.
Two kinds of interface declarations are possible in IDL:
namespace palmos { namespace support { interface IByteOutput interface IByteInput }}
You can also import interfaces from other IDL files and make the imported interfaces visible to the current interface. Both regular and forward declared interfaces can be imported. Imports are a good way to tell pidgen about specific custom types needed by the current interface.
To use imports, specify where the imported IDL file lives when pidgen is invoked with the -I flag. For example:
pidgen IWidget.idl -I C:\PDK\interfaces\widget
For detailed information on invoking PIDGen, see Invoking Pidgen.
Then in IWidget.idl, include the following code:
import <render/Font.idl>
When pidgen sees the import directive, it looks for Font.idl in the directory specified by the -I flag. More than one IDL can be specified, and pidgen will search all of them until it finds a match or exhausts all the directory options.
Any interfaces declared in, or imported by, imported interfaces are then available for use by the interface you’re declaring.
So far, you have the following code in Widget.idl:
import <render/Font.idl> namespace palmos { namespace widget{ interface IWidget { ... } }}
We can now populate IWidget with functions, events, and properties.
interface IWidget { methods: void RequestGestures(uint8_t gestures); void DefaultFont(SString font); void Poked(); void Leaned(); ... }
interface IWidget { ... events: void WidgetPoked(IWidget widget); void WidgetLeaned(IWidget widget); ... }
Events are methods that are called when specific things take place. For each event in your interface, pidgen generates a corresponding push method on its BnClass. For the example above, these would be BnWidget::PushPoked() and BnWidget::PushLeaned(). When implementing this interface, you can call these push functions whenever you would like to send out the event.
Clients that are interested in being notified of the event link to the event.
widget->Link(watcher, SValue("WidgetPoked", "HandleWidgetPoked");
The client also provides the HandleWidgetPoked() method, which is then called whenever the WidgetPoked() method is called by the widget object.
See Link Convenience Methods for more information about linking.
interface IWidget { ... properties: [readonly]SValue key; SString label; SValue fontDescription; [readonly]BFont font; bool enabled; bool hasFocus; int32_t interactionState; [readonly]uint8_t gestureFlags; SValue value; ... }
Pidgen also generates a Push function for all properties on an interface, very much like it does for Events. You should use these to tell others when your object's state changes, so they can link to your properties to monitor their value. This is an intrinsic part of the Binder's data-flow mechanism.
The inout attribute indicates that a parameter to a function or event is both an input and an output.
Here is an example of using attributes when declaring a method.
If in or out is not specified for a parameter, its direction is assumed to be in.
You can specify the local attribute on either the entire interface or on specific methods within the interface.
Here is an example of defining an interface that is entirely local.
[local] interface IColor { ... }
Here is an example of defining a specific method that is local.
interface IColorModel { methods: ... [local] int32_t LocalLookupColor(int32_t rgb); ... }
Local interfaces or methods generate less code, but have restrictions on how they can be used:
properties: IView view; [weak] IViewParent parent; methods: void GiveAView([in] IView view); void GiveAParent([in weak] IViewParent parent);
The resulting C++ code is shown below.
virtual sptr<IView> View() const; virtual void SetView(const sptr<IView>& value); virtual wptr<IViewParent> Parent() const; virtual void SetParent(const wptr<IViewParent>& value); virtual void GiveAView(const sptr<IView>& value); virtual void GiveAParent(const wptr<IViewParent>& value);
The C++ binding passes all interfaces as smart pointers; the default is to use a strong reference, so if you need to use a weak reference, be sure to specify the weak attribute.
[reserved] int32_t _reservedProperty1; ... [reserved] int32_t _reservedPropertyN; [reserved] status_t _ReservedMethod1; ... [reserved] status_t _ReservedMethodM;
Reserved properties and functions use the same syntax as normal properties and functions, but simply include the reserved attribute. There are no rules for the actual signatures of the reserved properties and methods, but the convention is to follow the example shown above.
When you decide to add a new method or property to your interface, you do so by replacing one of the reserved items with your new property or method.
An important thing to note: if the new property you’re creating is a readonly property, there’s an extra step to take. That’s because reserved properties get slots automatically reserved for their getter and setter methods, and read-only properties don’t need a setter method. So, to maintain binary compatibility, you need to do something like what’s shown here.
[readonly] IFoo myFooProperty; [reserved readonly] int32_t _reservedProperty1;
Since the reserved property is read-only, it saves one method slot, thereby restoring binary compatibility.
bool char string short long double float int8 int16 int32 int64 uint8 uint16 uint32 uint64 size_t ssize_t status_t bigtime_t off_t SValue SString
In addition, interface types are converted to sptr<> and wptr<> smart pointers, as described in the weak attribute.
If you want to marshal types other the ones listed, the available options are:
interface ISurface
{
typedef uint64 pixel_format;
properties:
[readonly]pixel_format arg
...
}
An out sequence will be completely overwritten by the method, while an inout sequence can be modified as desired by the method. Sequences specified as an in parameter are passed by reference, such as "<CODE>const SVector<> &</CODE>"). Sequences may also be returned, however, returning a sequence requires a copy constructor, so using an out parameter is more efficient.
A sequence must be named by a typedef before it can be used, as demonstrated here.
typedef sequence<int32_t> CategoryList; method: void GetCategories([out] CategoryList list);
The above IDL creates a typedef and member function in the resulting interface class as seen here.
typedef SVector<int32_t> CategoryList; void GetCategories(CategoryList *list);
The typedef is scoped within the interface class, so any external usage of the typedef must be qualified, using syntax such as ClassName::CategoryList
.
When using a sequence, SVector::AsValue()
and SVector::SetFromValue()
are called. These methods, in turn, call two helper functions. The helper functions are specialized for all the basic types. However, for custom types, you can use one of three helper macros to define the needed functions:
TYPE::AsValue()
and TYPE(const SValue &)
.
sptr<IBinder> menuBinder = m_menu->IMenu::AsBinder(); menuBinder->Link(this->AsBinder(), ...);
with the single link convenience method call shown below.
m_menu->LinkMenu(this->AsBinder(), ...);
interface ISurface { using namespace palmos::render; ... }
type.ASValue()
converts a type it into an SValue, and type.SetTo(SValue)
converts a type from an SValue). However, pidgen can include your enums or structs in the generated header if they are declared within the IDL file.
pidgen idlfilename
where idlfilename is the name of your IDL file.
You can type -h to see the list of flags available. Flags include:
import path | -I path
output-dir path | -O path
output-header-dir path | -S path
For example, the Jamfile for PDK/interfaces/app might start like this:
# Jamfile to build app interface files SubDir TOP platform interfaces PDK app ; # Build IDL-generated files InterfaceIdl IApplication.cpp : IApplication.idl : libbinder ; InterfaceIdl ICommand.cpp : ICommand.idl : libbinder ; ...
Make sure the generated files work before you specify them in the Jamfile.
namespace palmos { namespace support { interface IDatum { enum { READ_ONLY = 0x0000, WRITE_ONLY = 0x0001, READ_WRITE = 0x0002, READ_WRITE_MASK = 0x0003, ERASE_DATUM = 0x0200, OPEN_AT_END = 0x0400 }; enum { NO_COPY_REDIRECTION = 0x0001 }; properties: uint32_t valueType; off_t size; SValue value; methods: IBinder Open(uint32_t mode, [optional]IBinder editor, [optional]uint32_t newType); status_t CopyTo(IDatum dest, [optional]uint32_t flags); status_t CopyFrom(IDatum src, [optional]uint32_t flags); events: void DatumChanged(IDatum who, IBinder editor, off_t start, off_t length); } } } // namespace palmos::support
namespace palmos { namespace support { interface INode { enum { REQUEST_DATA = 0x1000, COLLAPSE_NODE = 0x2000, IGNORE_PROJECTION = 0x4000 }; enum { CREATE_DATUM = 0x0100, CREATE_NODE = 0x0200, CREATE_MASK = 0x0300 }; enum { CHANGE_DETAILS_SENT = 0x0001 }; properties: [readonly]INode attributes; SString mimeType; nsecs_t creationDate; nsecs_t modifiedDate; methods: status_t Walk([inout]SString path, uint32_t flags, [out]SValue node); events: void NodeChanged(INode who, uint32_t flags, SValue hints); void EntryCreated(INode who, SString name, IBinder entry); void EntryModified(INode who, SString name, IBinder entry); void EntryRemoved(INode who, SString name); void EntryRenamed(INode who, SString old_name, SString new_name, IBinder entry); }
namespace palmos { namespace support { interface IIterator { enum { BINDER_IPC_LIMIT = 0x05 }; enum { REQUEST_DATA = 0x1000, COLLAPSE_NODE = 0x2000, IGNORE_PROJECTION = 0x4000 }; typedef sequence<SValue> ValueList; properties: [readonly]SValue options; methods: status_t Next([out]ValueList keys, [out]ValueList values, uint32_t flags, [optional]size_t count); events: void IteratorChanged(IIterator it); }