SDPlugins are designed to allow you to
extend the functionality of applications that support them such as DesktopX and
ObjectBar. Examples of such Plugins would be virtual desktops, MP3 players,
analog clocks, email checkers, Recycle bin checkers, printer status monitors,
games, disk space monitors, CPU graphs, and anything else that needs to make
calls to the system or the Internet and talk back to the object it is attached
to or other objects running on the system. An SDPlugin will always attach itself
to the object (or bar) that it is part of. And when exported for use by others,
it will be included. This is done to decrease support.
This guide is designed to document how
SDPlugins are created. It is designed for software developers who are already
familiar with a programming language and how to create DLLs.
General information
Plugins must be compiled with
stdcall calling convention.
If you are compiling with MS Visual C++
you should also use “Multithread DLL” run-time library for Release version and
“Debug Multithread DLL” run-time library for the Debug version, since the
required modules are distributed with DX anyway and this way the plugin will be
a lot smaller.
The constants mentioned in this document
are declared in the SDPlugin.h header files.
Plugin lifetime
This chapter discusses the differences
between the plugin lifetime and the instance (of the plugin) lifetime.
The plugin dll is loaded when an object
needs the plugin. The SD_INITIALIZE_MODULE is sent through the SDMessage
callback (see below).
The plugin dll is unloaded when the last
object using the plugin is deleted or the plugin removed, so no object needs the
plugin. SD_INITIALIZE_MODULE is sent through the SDMessage.
When a plugin is selected to be used in
an object an instance of the plugin is created and associated to the object.
More instances can be added to an object (i.e. an EmailCheck plugin can be added
more times to handle more email accounts).
When an instance of the plugin is
created the plugin receive SD_CREATE_PLUGIN. Note that at this point the plugin
should not start its action (i.e. a clock plugin should not start displaying the
hour), i.e. the DX object is not yet created. This message is sent for the
plugin to initialize instance data, allocate variables and such.
The plugin instace actually starts
working when it received the SD_INITIALIZE_PLUGIN message. Infact now it
receives the object HWND and the rect size of the object. It means that the
object has been created and the plugin should start “running”.
After the instance is created
(SD_CREATE_PLUGIN ) but before it is running (SD_INITIALIZE_PLUGIN), the plugin
can be configured. This can happen in three ways:
Directly by the user: the user just
clicked “Configure” in the plugin section of the object properties. At this
point SD_CONFIGURED is sent to the plugin and it would tipically open a
config dialog to configure the instance settings.
When an object is loaded from the
disk (i.e. the user loads a theme or package with objects using your
plugin). Now the instance config data is tipically loaded config data files
that the plugin previously saved in the theme directory and the host saved
them in themes or packages. The plugin knows that it should retrieve the
saved instance config data when it receives the SD_LOAD_DATA message.
By the host through
SD_DUPLICATE_PLUGIN message. This is probably the only hard part of the
whole specs. Basically SD_DUPLICATE_PLUGIN is used overall by DesktopX to
support special operations, when an object is duplicated and when an object
is configured. On cloning an object, a new object is created and so, new
plugin instances are created. These new instances need to be configured the
same as the original object/instances. Infact SD_DUPLICATE_PLUGIN contains
references of the original plugin instance to copy (in the sense of
“configuring” it). Why is SD_DUPLICATE_PLUGIN needed on configuring an
object?? When you open the object properties panel, you can still use all
existing objects and so the object you are configuring. You can change its
graphics, settings, add, remove, configure plugins and the configured object
is still working the same way. All changes are applied when you actually
click “Apply” or “Ok”. If you click “Cancel” the changes are ignored. To
support this mechanism DesktopX performs these tasks:
When the user opens the object
properties panel, a new “dummy” plugin instance is created.
SD_DUPLICATE_PLUGIN is sent to configure this new instance as the
original instance.
This “dummy” instance is used
to be configured, but will never actually receive SD_INITIALIZE_PLUGIN.
When the user click Apply, the
original object is destroyed with all his plugin instances. A new object
is created with new settings (graphics, options, and new added plugins,
removed plugins or differently configured plugin instances). New plugin
instances are now created for the object. These are the final “real”
instances and they are configured with SD_DUPLICATE_PLUGIN: the new
plugin instance need to be confgiured the same as the “dummy” instances
used in the properties panel.
The host destroys the “dummy”
instances, while the new instances receive SD_INITIALIZE_PLUGIN and
start working.
This could
sounds difficult, but it is just to explain how the host works with plugins, you
can simply ignore all of the above explanations and just follow and respect the
plugin messages, just keep in mind that SD_CREATE_PLUGIN initializes the plugin
config data (you would set this to default settings), SD_CONFIG configures this
data through the user’s input, SD_DUPLICATE_PLUGIN configures a plugin instance
the same way as another plugin instance, SD_LOAD_DATA configures the plugin
instance from disk saved data. SD_INITIALIZE_PLUGIN finally “starts” the plugin
(i.e. a timer starts running, the email check plugin starts checking for emails,
etc.).
When the plugin instance is removed by
the object, or the object is deleted, SD_TERMINATE_PLUGIN is sent for each
instance.
Note that there are plugin’s own
configuration data and config data needed by the host. I.e. if the plugin is a
drawing plugin, that is, it draws the object graphics (i.e. analog clock
plugin), the host needs to know it to correctly allocate the graphic buffers to
be used by the plugin. So, the host needs to know if the plugin will sublcass
the object. Plugins pass this information to the host in two cases:
When the plugin instance is created
(SD_CREATE_PLUGIN). The plugin should tipically set default flags and/or
flags that won’t change.
When the plugin is configured
(SD_CONFIG). The plugin can here change the current passed flags (saved in
SD_CREATE_PLUGIN).
This way plugin options are “per
instance” rather than “per plugin”. So, an instance could be set as drawing
plugin, another instance of the same plugin could be not a drawing plugin (same
for the subclass option and the other ones).
As mentioned plugins can store data in
extern files (config files, graphics, etc). These files should be stored in the
directory returned by SD_GET_THEME_DIRECTORY.
SDMessage
SDMessage is used by the host to send to
the plugin callback messages.
objID is used by the host to uniquely
identify the objects. It must be passed to the host when using callbacks.
pluginIndex is an object identifier used
by the plugin. It is modified by the plugin itself and is passed by the host on
all messages. The plugin can use it to directly reference the plugin local data
and settings.
param1 and param2 are message-specific
parameters.
Returns TRUE if the message is supported
and is processed.
Returns FALSE if the message is
unsupported or not processed.
This is the list of the messages the
host will send:
Called once
when the plugin module is loaded by the host. (By name, should be called when a
plugin is going to use this module and no other plugins already exist that use
this module.)
The param1
contains a pointer to the SDHostMessage function the plugin will use to
communicate with the host. See below for more information.
SD_TERMINATE_MODULE
Called once
when the plugin module is unloaded by the host. (When the last plugin that uses
this module is destroyed, this should be called).
SD_QUERY_PLUGIN_INFO
The host queries the plugin to
obtain information about it. All this data is returned in the structure which
is passed in the param1. This structure is allocated by the host.
SD_PLUGIN_INFO* pd = (SD_PLUGIN_INFO*) param1;
struct SD_PLUGIN_INFO
{
DWORD host; // set by the host to identify whether the
host is OB or DX:
//SD_HOST_DESKTOPX or SD_HOST_OBJECTBAR
DWORD host_version; // set by the host,
specifies the version of the program
// 100 =
1.0, 101 = 1.01, 110 = 1.10
char plugin_name[256];
char plugin_author[256];
DWORD plugin_version; // 100 = 1.0, 101 = 1.01,
110 = 1.10
DWORD supported_platforms; // SD_OS_ALL or any
combination of the following values:
// SD_OS_95, SD_OS_98, SD_OS_ME, SD_OS_NT4, SD_OS_2000,
SD_OS_XP
DWORD supported_hosts; //
SD_HOST_DESKTOPX and/or SD_HOST_OBJECTBAR
};
Returns TRUE if the plugin is host compatible. Returns FALSE
if the plugin isn't compatible with the host.
Note: this message could be simply used to query some info
about the plugin, so the host can send it without then really initialize the
plugin (SD_INITIALIZE_MODULE). If the plugin absolutely needs to initialize even
if only to be queried, it should use DllMain and not SD_INITIALIZE_MODULE.
SD_QUERY_CUSTOM_STATES
char
*szCustomState = (char*) param1;
int
iCustomStateIndex = (int) param2;
Copy in szCustomState the name
of the required cutom state. Return FALSE to cancel or stop the enumeration. By
returning FALSE, szCustomState will be ignored.
This message is sent if the
plugin is a drawing plugin. The plugin has been already associated to the object
and it is already configured. The host must now know the desidered object size.
SIZE *sz = (SIZE *) param1;
SD_CREATE_PLUGIN
pluginIndex
should be initalized here, allocating memory and storing pointers to that memory
in it as needed.
DWORD *plugin_flags = (DWORD*) Param1; // any
combination of the flags described in “Plugin flags” below.
SD_DESTROY_PLUGIN
pluginIndex
should be de-initalized here; any memory it associated with it should be freed,
along with any other local data that was allocated.
CopyMemory((*pluginIndex), (*pluginIndex_OriginalPlugin), sizeof(MYSTRUCT));
//copies over the memory from the old plugin’s PIN data to the new plugin’s PIN
data, so they’re identical
//Note further
processing might be needed if more pointers/handles are located within the
memory pointed to by pluginIndex!
//OR, if all
that’s stored in pluginIndex is a number or something that has nothing to do
with memory:
(*pluginIndex)
= (*pluginIndex_OriginalPlugin);
SD_INITIALIZE_PLUGIN
Struct SD_PLUGIN_INIT
{
HWND hwnd; // the object ‘s
hwnd
RECT rcObject; // the portion of
the window occupied by the object
}
SD_PLUGIN_INIT *init_data = (SD_PLUGIN_INIT *) param1;
The plugin should initialize
here plugin local data, timers, etc.
SD_TERMINATE_PLUGIN
This message should just be
used to notify the plugin that it is being uninitalized. The plugin must here
destroy timers, etc.
This message is sent when the plugin should load the
configuration data for the object. In szInstanceID the host writes the
identifier of the plugin object instance that the plugin itself wrote in
SD_SAVE_DATA. This is needed to identify plugin instances into different
objects.
This message is sent when the plugin should save the
configuration data, i.e. before unloading or before exporting. The plugin will
need to send SD_REGISTER_FILE messages to the host for each file that needs to
be associated with the object.
The plugin should write in szInstanceID an instance
identifier that the plugin will use on SD_LOAD_DATA time to identify the
different instances if the plugin is working for more objects.
If the user is exporting, the plugin could choose to save
config data differently, i.e. an email notify plugin won't save the email
login/pass information, since the objects could be distributed around the web.
SD_CONFIGURE
DWORD *plugin_flags = (DWORD*) param1; // as defined in
SD_CREATE_PLUGIN, the plugin can here modify its flags depending on the
configuration.
HWND hwndParent = (HWND) param2;
The plugin needs to popup a dialog to be configured.
SD_STATE_CHANGING
char *szNewState = (char *) param1;
Here the developer can either return FALSE which would allow
the host change the item/object’s state to the new state, or the developer can
return TRUE and prevent the host from changing the item’s state.
If the
developer desires to return TRUE to prevent the host from changing the item’s
state, the plugin will probably want to set it manually to one its custom states
or another standard state. To do this the plugin must copy the new state string
identifier in szNewState. If an empty string is set, the plugin will simply
cancel the state changing request, nor the original state nor another modified
one is applied.
In SDPlugin.h
are defined these standard state identifiers.
Struct SD_DRAW_INFO
{
HDC hdc;
HBITMAP hbitmap;
RECT rcObject; // bounding rectangle of the
area occupied by the object in the HDC
// if the host is
DesktopX, this rectangle will be always (0, 0, width, height)
char *szCurrentState; // current state identifier
int *iRender; // indicates
the rendering method the host should use.
//
SD_RENDER_OPAQUE: the host will perform a plain rectangular rendering
//
SD_RENDER_TRANSPARENT: the host will render considering pink areas (255, 0, 255)
as transparent
// SD_RENDER_PER_PIXEL_ALPHA: the host will render using the 4th
channel for 256-levels transparency information.
}
SD_DRAW_INFO *drawInfo = (SD_DRAW_INFO *) param1;
The plugin must draw into the passed HDC. Note that the
plugin should’t consider what is already in the HDC. It should draw a complete
frame, eventually using pink area (255, 0, 255) when it needs transparency.
After the plugin returns the host will actually draw in the background, but this
is not plugin task.
If the host is DesktopX, the HBITMAP
will always be a 32 bit memory DIB. In this case the plugin can directly modify
the color arrays and if needed set *bPerPixelAlpha TRUE to enable the host to
render in per-pixel alpha blending mode. The bitmap so drawn is then passed to
the DesktopX renderer, that is, the plugin will never draw to the HDC directly.
If the plugin draws in opaque mode
(not using per pixel information), it can use pink regions (RGB = 255, 0, 0) to
indicate transparent regions.
SD_COMMAND
This message is sent to plugins registered to perform a
custom action with the SD_FLAG_CUSTOM_ACTION flag.
SD_WINDOW_MESSAGE
The plugin receives this
message from the host if it has registered itself to hook window messages.
MSG *msg = (MSG *) param1;// note that in the current
implementation the time and pt members are not used.
DWORD*
returnValue = (DWORD*) param2;
The message is
not yet processed by the host. The plugin can simply retrieve the info or modify
the message parameters.
Returns TRUE
if the message must be not processed by the host. The plugin must write in
returnValue the desidered value to return to the window proc.
Returns FALSE
if the message must be processed by the host.
SD_MODE_CHANGED
The host sends this message to
all plugins when the current mode is changed. Param1 is TRUE if the host is in
User mode, FALSE if it is in Edit/Design mode.
Plugin flags
The flags are used to configure the
plugin instance. The default flag is set on SD_CREATE, then it can be configured
on SD_CONFIGURE and SD_LOAD_DATA.
These are the possible values:
SD_FLAG_DRAW
= The plugin will draw directly the object appearance, it is a drawing plugin
(i.e. analog clock). If a drawing plugin is selected for an object, the user
won't be able to directly setup the messages/states in the GUI. The appearance
will be only configurable through the plugin configuration dialog.
SD_FLAG_SET_SIZE = This flag can be used by drawing plugins, if the plugin needs
to determine the initial object size, instead of using the one specified by the
user in the object size settings (that will be disabled). In this case the
plugin will receive SD_GET_OBJECT_SIZE when the host needs to know those values
to create or modify the window or do other internal adjustements.
SD_FLAG_CUSTOM_ACTION = The plugin will perform a custom action (i.e. control
Winamp). It is a command plugin. If a command plugin is selected for an object,
the command class will switch automatically to "Custom class" and the
"Configure" button will open the plugin configuration panel. See SD_COMMAND
below for more details.
SD_FLAG_CUSTOM_STATES = The plugin has custom states that it wants host to
observe*/
SD_FLAG_ONLY_CUSTOM_STATES = Items/objects using this plugin should only show
the custom states when the users is editing the item's appearance.
SD_FLAG_FILTER_STATE_CHANGES = The plugin receives SD_STATE_CHANGING
SD_STATE_CHANGED that inform the plugin about state messages. The plugin can
modify and/or remove them as they get sent.
SD_FLAG_SUBCLASS = The plugin should set this flag to indicate if it wants to
get and set messages from the window procedure sent from the host through
SD_WINDOW_MESSAGE. Note that the plugin MUST NOT subclass the object window.
This hooking service should be used instead.
SD_FLAG_DRAW_PPALPHA = The plugin
informs the host that it will use the 4th byte of the 32bit HBITMAP
to supply per-pixel alpha blending information.
SD_FLAG_MORE_INSTANCES = By default the
host will avoid that more instances of the same plugin are added to an object.
This flag will make it possible to add more than one instance.
Tipical order of messages
SD_QUERY_PLUGIN_INFO
Can be called only for querying
purpose, in this case the other messages won’t be called.
SD_INITIALIZE_MODULE
Only called if this is the first
plugin from this DLL being loaded
SD_CREATE_PLUGIN
Setup pluginIndex, allocate memory
SD_LOAD_DATA
SD_CONFIGURE
These are the two ways a plugin
instance can be configured, infact the plugin can here modify the plugin
flags.
SD_QUERY_CUSTOM_STATES
Custom states actually depend on the
plugin configuration, so these should be set when the plugin is first
time created and when it is configured.
SD_INITALIZE_PLUGIN
SD_SAVE_DATA
Save plugin data to object directory
or plugin directory. Can be called at any time between SD_INITIALIZE and
SD_TERMINATE.
SD_TERMINATE_PLUGIN
Uninit plugin, stoping any times it
has, etc
SD_DESTROY_PLUGIN
Destroy pluginIndex, deallocate
memory
SD_DEINIALIZE_MODULE
Only called if this is the last
plugin from this DLL being unloaded
In order to
support Apply/Cancel/Ok in the object configuration panel the plugin is
requested to duplicate its plugin data through SD_DUPLICATE_PLUGIN.
SDHostMessage
SDHostMessage is used by the plugin to
communicate with the host or request certain tasks (i.e. move an object, get the
theme directory, etc.).
The object directory is the directory that contains the
plugin custom files if needed, the files that will be registered through
SD_REGISTER_FILE.
This folders should be part of
the theme, and should be the folder where the object/theme is being saved to.
E.g. something like DesktopX\CurrentTheme\ etc etc.
This is there to save
object/theme-specific “settings” which change from object to object, theme to
theme, like the color of a clock or something like that.
SD_GET_PLUGINS_DIRECTORY
char *szDir;
param1 = (DWORD) szDir;
Host would fill szDir with the path of the common plugins
folder for DX or for OB… like DesktopX\SDPlugins\ or ObjectBar\Plugins\. The
plugin could then create subfolders for itself as needed, so it can store data
common to everything that uses this plugin.
Again this is here so the plugin knows where to save general
“data”, which should be common no matter what theme/object uses it, such as for
the command line plugin’s history, etc.
SD_LOAD_IMAGE
typedef struct
SD_IMAGE_INFO_
{
char
fileName[MAX_PATH]; // input: full path
HBITMAP hb;
// output: receives a 32 bit bitmap
int width;
// input: set this to the extract icon size if the file is an icon. output:
image width
int height;
// output: image height
BOOL
bHasAlpha; // output: true if the image contains per pixel alpha values, false
otherwise
} SD_IMAGE_INFO;
SD_IMAGE_INFO
ii;
Param1 = (DWORD) ⅈ
Loads a .ico, .bmp, .jpg or .png (all formats supported) file
to a 32 bit bitmap.
SD_REDRAW
Param1 = objID;
Sends a request to update/redraw
the object (i.e. the clock plugin would call this each second).
The plugin will receive back a SD_DRAW message when the host
is ready to have the object redrawn.
Depending by the implementation of the host, the plugin could
receive SD_DRAW back immediately or not.
SD_GET_STATE
Param1 = objID;
Param2 = (DWORD) szCurState;
The host copies in szCurState the current state identifier.
SD_SET_STATE
Would pass a string to the host to set the current state to
that custom state if it exists.
The plugin should pass a pointer to a RECT struct
(lprcContainerRect) in Param2, which the host will fill with the rectangular
area of the screen occupied by the container HWND, the object/bar that owns the
plugin whose ID is passed in Param1.
SD_GET_OBJECT_RELATIVE_RECT
Param1 = objID;
Param2 = (RECT*) lprcObjectRect;
The plugin should pass a pointer to a RECT struct
(lprcObjectRect) in Param2, which the host will fill the rectangular area
occupied by the object in the HWND (the container). This is could not be the
same rectangle passed on SD_DRAW, because that rectangle refers to the area
occupied in the passed HDC.
Allows the plugin to process mouse handling and such
if it subclasses, and could also be used for some other purposes as well.
The plugin sends this message to the host while processing
the SD_SAVE_DATA message. This message will need to be sent once for every file
that needs to be registered. The plugin will need to register all files that
will need to be saved on every SD_SAVE_DATA message.
The plugin creates an object. It must make sure all required
graphic and sound files are present in the theme directory.
szObjectDefinition contains the multiline (\r separated)
object string from “OBJECT” to “!OBJECT”.
IOversion is the version of the IO syntax used in
szObjectDefinition. You should check what version is used by the latest DX
version and use the same number and latest syntax. You can find the version
number in the first line of theme.ini.
SD_PACK_IMAGE
HBITMAP *hb =
(HBITMAP *) param1;
Converts a
double height 32 bit bitmap to a single height 32 bit bitmap with packed alpha.
SD_SET_OBJECT_POS
typedef struct SD_SOP_INFO_
{
int x;
int y;
int width;
int height;
DWORD flags; // any combo of these flags:
// SD_SOP_MOVE: x and y are used
// SD_SOP_SIZE: width and height are used (drawing plugins
only)
// SD_SOP_SHOW: shows the object
// SD_SOP_HIDE: hides the object
// SD_SOP_ONTOP: sets the object on top of its zorder class,
i.e. if it is a desktop level object, it will be placed on top of all other
desktop level objects
} SD_SOP_INFO;
BOOL (CALLBACK *EnumObjectsProc) (DWORD ObjID, DWORD lParam);
{
To continue
enumeration, the callback function must return TRUE; to stop enumeration, it
must return FALSE.
}
param1 = (DWORD) EnumObjectsProc;
param2 = (DWORD) appDefinedValue;
SD_DELETE_OBJECT
Param1 = objID; // object ID
Param2 = (DWORD) (BOOL) bDeleteChildren; // set
TRUE to delete also all children of the object referred by objID
Returns TRUE if no errors occurred, returns FALSE if: - the
objID was invalid or – the object was being configured.
The host copies in szObjectGUIID the object ID defined by the
user in the GUI.
SD_SET_OBJECT_GUIID
char szObjectGUIID[100];
Param1 = (DWORD) objID;
Param2 = (DWORD) szObjectGUIID; // inThe host sets the object ID defined in the GUI as
szObjectGUIID. This message is the opposite of SD_OBJID_FROM_GUIID.
SD_USER_MODE
Returns TRUE if DesktopX is in User mode, FALSE if it is in
Design/Edit mode.