REALbasic plugins are code resources that extend the features available to a REALbasic user.
The things that can be written with plugins are:
Each of these will be explained in more detail below:
A plugin is a resource file with a Creator type of 'RBv2' and a file type of 'RBPl'. There is an icon associated with this by REALbasic (though if you upgraded from a previous version, you may need to rebuild your desktop to see the icon).
Plugin code goes into the following resource types:
The first plugin resource in a file must have id 128, the second plugin 129, etc. Usually, you'll put all your code into a single plugin resource (since it may contain any number of classes and functions).
All plugins must have a routine called PluginEntry - this routine should notify REALbasic about all of the features that the plugin provides.
A simple PluginEntry routine might be something like:
void PluginEntry(void)
{
REALRegisterMethod(&add5defn);
}
This plugin has defined a single method, and it calls the REALRegisterMethod function to define it.
The currently supported registration routines are:
You add a new global method to REALbasic with the REALRegisterMethod function. This takes a single parameter, which is a REALmethodDefinition structure. That structure contains three elements:
REALnoImplementation
for the second parameter when it does not apply.
Example. To register the following function:
static int add5func(int v)
{
return v + 5;
}
...you would need a REALmethodDefinition as follows:
REALmethodDefinition add5def = {
(REALproc) add5func,
REALnoImplementation,
"Add5(num as Integer) as Integer" };
And then, in your PluginEntry function, you'd simply call REALRegisterMethod( add5def )
. Note that the definition ("add5def" in the example above) must be either global or static, i.e., it must not go out of scope when PluginEntry exits, since RB depends on it sticking around for the lifetime of the application.
The REALRegisterClass function requires a reference to a REALclassDefinition structure. For example:
REALclassDefinition ShellClass = {
kCurrentREALControlVersion,
"Shell", // name of class
nil, // no superclasses
sizeof(ShellData), // size of our data
0,
(REALproc) ShellConstructor, // constructor
(REALproc) ShellDestructor, // destructor
ShellProperties, // properties
sizeof(ShellProperties) / sizeof(REALproperty),
ShellMethods, // methods
sizeof(ShellMethods) / sizeof(REALmethodDefinition),
// other fields left nil -- no events etc.
};
The fields of the REALclassDefinition structure are as follows:
Use of event handlers, interfaces, and bindings is not yet documented.
Note that the class definition ("ShellClass" in the example above) must be either global or static, i.e., it must not go out of scope when PluginEntry exits, since RB depends on it sticking around for the lifetime of the application. The same applies to all the subordinate data structures (properties, methods, etc.).
A REALproperty has the following fields:
Example:
REALproperty boxProperties[] = {
{ "Appearance", "BackColor", "Color", REALpropInvalidate,
REALstandardGetter, REALstandardSetter, FieldOffset(facelessData, fillColor) }
};
Class or control methods are declared exactly the same as new global methods. E.g.,
REALmethodDefinition boxMethods[] = {
{ (REALProc) makeRed, REALnoImplementation, "MakeRed()" },
{ (REALProc) colorComponent, REALnoImplementation, "ColorComponent(v as Integer) as Integer" }
};
REALevent boxEvents[] = {
{ "Action" }
};
A REALevent structure merely has the declaration for the event, which is in the same form as a method declaration. Adding an event to a class or control allows you to provide "callbacks" to your users; the user writes RB code for an event you've declared, and you can invoke that code via REALGetEventInstance.
The REALRegisterClassExtension function also uses a reference to a REALclassDefinition structure. So declaring an extension to an existing class is just like declaring a new class, with the following exceptions:
The third point is especially important; it means that if your class extension must not contain custom data that requires explicit allocation or deallocation. (RB will, however, initialize all your custom data to zero and release the data itself when the object dies; but it will not release any additional memory you may have allocated.)
The last point is a bug in REALbasic which causes the following: if the user uses nothing from your plugin except class extensions, RB will not recognize that your plugin has been used at all and will therefore fail to copy your plugin code into built apps. This is something we hope to fix someday, but until then, add a global method (InitFoo or IsFooAvailable or whatever) and require your users to call it at some point in their code.
The REALRegisterControl function is passed a reference to a REALcontrol structure.
e.g.,
REALcontrol boxControl = {
kCurrentREALControlVersion,
"box",
sizeof(boxData),
REALcontrolAcceptFocus | REALcontrolFocusRing,
169,
170,
32,
32,
boxProperties,
sizeof(boxProperties) / sizeof(REALproperty),
boxMethods,
sizeof(boxMethods) / sizeof(REALmethodDefinition),
boxEvents,
sizeof(boxEvents) / sizeof(REALevent),
&boxBehaviour
};
The fields of the REALcontrol structure are as follows:
Use of event handlers, interfaces, and bindings is not yet documented.
Note that the control definition ("boxControl" in the example above) must be either global or static, i.e., it must not go out of scope when PluginEntry exits, since RB depends on it sticking around for the lifetime of the application. The same applies to all the subordinate data structures (properties, methods, etc.).
REALcontrolBehaviour boxBehaviour = {
boxInit,
nil,
boxDraw
/* other fields left nil */
};
The entry points are as shown in the following table (in this order).
function | description | prototype |
---|---|---|
initFunction | called when the control is initialized | void initFunction(REALcontrolInstance) |
disposeFunction | called when the control is disposed | void disposeFunction(REALcontrolInstance) |
redrawFunction | called when the control needs to be redrawn | void redrawFunction(REALcontrolInstance) (MacOS) void redrawFunction(REALcontrolInstance instance, REALgraphics context) (Win32) |
clickFunction | called when the control is clicked on | Boolean clickFunction(REALcontrolInstance, int x, int y, int modifiers) |
mouseDragFunction | called while the mouse is held down after clickFunction returned true | void mouseDragFunction(REALcontrolInstance, int x, int y) |
mouseUpFunction | called when mouse is released after clickFunction returned true | void mouseUpFunction(REALcontrolInstance, int x, int y) |
gainedFocusFunction | called when the control gains the focus | void gainedFocusFunction(REALcontrolInstance) |
lostFocusFunction | called when the control loses the focus | void lostFocusFunction(REALcontrolInstance) |
keyDownFunction | called when the user presses a key while the control has the focus | Boolean keyDownFunction(REALcontrolInstance, int charCode, int keyCode, int modifiers) |
openFunction | called when the control is opened | void openFunction(REALcontrolInstance) |
closeFunction | called when the control is closed | void closeFunction(REALcontrolInstance) |
backgroundIdleFunction | called periodically when nothing much is going on | void backgroundIdleFunction(REALcontrolInstance) |
drawOffscreenFunction | called to draw the control into an offscreen graphics context | void drawOffscreenFunction(REALcontrolInstance, REALgraphics context) |
setSpecialBackground | called to paint a special background for the control | void setSpecialBackground(REALcontrolInstance) |
constantChanging | called when the definition of a named constant changes | void constantChanging(REALcontrolInstance, REALstring constantName) |
droppedNewInstance | called on a newly cloned control in the IDE | void droppedNewInstance(REALcontrolInstance) |
There are roughly 100 functions provided by the header files in the plugin SDK for your use. These are documented in the API Reference. Note that not all API functions are available on all platforms. If you're writing a multi-platform plugin, you may wish to use the macros TARGET_68K, TARGET_PPC, TARGET_WIN32, and TARGET_CARBON to conditionalize your code.
Control plugins should provide a normal and depressed button image for the tool palette as 'PICT' resource 128 and 129, respectively. The normal button image should be based on 'PICT' 297 in the REALbasic application's resource fork. The bevelled button background will appear when your plugin is running in version 2.1.2 (or earlier) of the IDE; in RB 3.0 and later, it will be automatically stripped away to make a nice transparent icon in the tool palette.
A plugin file can specify additional resources to be incorporated into the built Mac OS application via a 'PLRm' resource.
A PLRm resource is just a list of resource type and id pairs - these resources will be incorporated into any built applications that utilise the plugin. Note that your resources (the ones incorporated into built applications) must use resource IDs above 15000; resource IDs below 15000 are reserved for REAL Software and the operating system.
The PLRm resource must have the same id as the associated plugin (usually 128). A ResEdit TMPL resource for the PLRm resource can be found in the GetFolder.rsrc file (also the REALbasic application itself).
When accessing your plugin's resources, remember that you may be operating in an environment where there are many resource forks open. It's important to code defensively (don't make assumptions about the state of the resource chain) and nicely (don't change things more than necessary). You are guaranteed that your resource fork is the current one when PluginEntry is called. After that, you're guaranteed that your resource fork is still open, but it may not be the current one. Either of the following two approaches should be safe:
1. Load your resources in your PluginEntry, then detach them with DetachResource (and store them in global variables). You should not need your resource fork after that. The drawback to this approach is that it consumes memory even when your plug-in is not being used; if your resources are large, consider the next approach.
2. In PluginEntry, store the value of CurResFile() in a global variable:
gMyResFile = CurResFile();
...and then, in any other plugin method that needs to access your resource fork, save the old current res file, switch to yours, and restore the old one when you're done:
short oldResFile = CurResFile();
UseResFile(gMyResFile);
// ...load something from your resource fork...
UseResFile(oldResFile);
If you follow these guidelines, your plugin will work robustly, and will also avoid causing any problems for other (less robust) plugins.
For example, if you create a class called "NiftyText" deriving from StaticText, then when a user selects any StaticText in a window, they'll be able to pick NiftyText from the menu.
Your best bet is to simply store whatever data in your object means (to you) "invalid", and check for this state on all subsequent function calls. But if you really need to be able to make a constructor that can fail, please submit a feature request with REALbugs.
HINSTANCE hInst = GetWindowLong( REALGetControlHWND(myControl), GWL_HINSTANCE );
So, if you have a factory function that creates and returns an object, just call REALnewInstance and return the result. Or, if you have a class method that creates a sub-object and stores it internally, just call REALnewInstance and store the result. But if you have a method that both returns the object to the caller, and stores it away in your class data, then you must call REALLockObject (or one of the type-specific locking functions) since there are now two references -- one in the caller's code, and one in your class data.
Similarly, if you have an object reference in your class data, and you provide a "getter" function that gives the caller a reference to that object, you must remember to lock it before returning the reference.
The one other time you may need to lock an object is if you're doing some complex operation on an object passed to your code, and something in those operations may invoke user code (e.g., invoking an event handler). Keep in mind that you don't know what the user's code may do; it may in fact release its reference to the object you're working on, causing it to be destroyed right out from under you. To deal with this danger, lock the object while you're working on it, and unlock it when done. This will ensure that the object does not get destroyed, even if the user code releases all references to it.
Call REALUnlockObject (or similar) to unlock an object whenever you reduce the number of references to the object. E.g., if you have a REALobject variable which you're about to set to nil or some other value, and assuming you locked the previous reference at some point, unlock it before assigning the new value. You'll also need to unlock all object references in your class destructor. Basically, every where you get a lock, you must somewhere unlock it.
To summarize: the lock count must match the number of references to the object. If you fail to lock, the application will crash; if you fail to unlock, it will leak memory.
As a result, it is essential that your custom data structures have the same size on all platforms. Use padding if necessary to make them the same. And if you use the FieldOffset macro to fill in parameters for your property definitions, you must also make sure that the offset of each such element within your custom data structure is the same on all platforms.
Another reason your Win32 classes or controls might crash is due to a bug in Win32 plugin support: if you have a class B that derives from class A, the data for class B ought to be stored after all the data from class A. This works properly on the Mac, but on Win32, the data from class B actually overlaps that of class A, which obviously causes problems. We hope to fix this soon (for REALbasic 3.1).
The correct calling conventions are ensured by specifying the following pragmas:
// Compile with Metrowerks-ish/Think C calling conventions:
// Turn "MPW C" option off, and return pointers in D0 rather than A0.
#pragma d0_pointers on
#pragma mpwc off
These pragmas are specified in the latest version of REALplugin.h, a header file normally included by all plugin code. So if you have the latest SDK, you probably don't need to worry about this.
3.0.1a2 (24 Jan 2001):
3.0.1a1 (19 Jan 2001):
2.1.1a5 (14 Nov 2000):
2.1.1a4 (16 June 2000):
2.1.1a3 (12 June 2000):
2.1.1a2 (2 June 2000):
2.1.1a1 (31 May 2000):
2.1.0 (31 Mar 2000):
#pragma pointers_in_d0
to the apparently-more-compatible #pragma do_pointers on
.
24 Mar 2000:
r35a: