Copyright (c) Hyperion Entertainment and contributors.

Callback Hooks

From AmigaOS Documentation Wiki
Revision as of 22:09, 26 March 2014 by Steven Solie (talk | contribs) (Created page with "== Callback Hooks == The callback feature provides a standard means for applications to extend the functionality of libraries, devices, and applications. This standard makes ...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Callback Hooks

The callback feature provides a standard means for applications to extend the functionality of libraries, devices, and applications. This standard makes it easy for the operating system to use custom modules from different high level programming languages as part of the operating system. For example, the layers library, which takes care of treating a display as a series of layered regions, allows an application to attach a pattern function to a display layer. Instead of filling in the background of a layer with the background color, the layers library calls the custom pattern function which fills in the layer display with a custom background pattern.

Callback Hook Structure and Function

An application passes a custom function in the form of a callback Hook (from <utility/hooks.h>):

struct Hook
{
    struct MinNode h_MinNode;
    ULONG (*h_Entry)();     /* function entry point */
    ULONG (*h_SubEntry)();  /* secondary function entry point (not usually used) */
    VOID *h_Data;           /* owner specific */
};
h_MinNode
This field is reserved for use by the module that will call the Hook.
h_Entry
This is the address of the callback function. Since AmigaOS 4.0, all callbacks adhere to the PowerPC SysV ABI which is stack based and therefore the order of the parameters is important. When AmigaOS ran only on 68000 based CPUs, this entry would point to a hook stub which would take the arguments from 68000 registers A0, A1, and A2, push them on the stack and finally call h_SubEntry.
h_SubEntry
This is the address of the secondary function entry point. It should only used when emulating 68000 code.
h_Data
This field is for the application to use. It could point to a global storage structure that the callback function utilizes.

There is only one function defined in utility library that relates to callback hooks:

uint32 CallHookPkt(struct Hook *hook, APTR object, APTR paramPkt);

CallHookPkt will always ensure the correct procedure is used to invoke a callback. If the callback points to PowerPC code, it will be called directly. If the callback points to 68000 code, it will invoke the emulator and take the appropriate action.

Simple Callback Hook Usage

A Hook function accepts the following three parameters in the order specified:

  1. Pointer to the Hook structure.
  2. Pointer to an object to manipulate. The object is context specific.
  3. Pointer to a message packet. This is also context specific.

For a callback function written in C, the parameters must appear in this order:

myCallbackFunction(Pointer to Hook,      // A0 in 68000
                   Pointer to Object,    // A2 in 68000
                   Pointer to message);  // A1 in 68000

A callback function is executed on the context of the module that invoked it. This usually means that callback functions cannot call functions that need to look at environment specific data. For example, Printf() needs to look at the current process's input and output stream. Entities like Intuition have no input and output stream. To find out which context your callback is running on use the FindTask() function from Exec.

The following is a simple function that can be used in a callback hook which indicates which context it is running on.

uint32 MyFunction (struct Hook *h, VOID *o, VOID *msg)
{
    struct Task *task = IExec->FindTask(0);
 
    /* Debugging function to send a string to the serial port */
    IExec->DebugPrintF("MyFunction() running on Task %s\n",
      (task->tc_Node.ln_Name == NULL) ? "NoName" : task->tc_Node.ln_Name);
 
    return 1;
}

The next step is to initialize the Hook for use. This basically means that the fields of the Hook structure must be filled with appropriate values. There are two ways to accomplish this. Either use the AllocSysObject() function with an object type of ASOT_HOOK or declare the hook statically and manually initialize the fields.

The following example shows how to allocate a Hook structure dynamically:

struct Hook *hook = IExec->AllocSysObjectTags(ASOT_HOOK,
  ASOHOOK_Entry, func,
  ASOHOOK_Data, data,
  TAG_END);

The following simple function initializes a static Hook structure.

/* This simple function is used to initialize a Hook */
VOID InitHook (struct Hook *h, uint32 (*func)(), VOID *data)
{
    /* Make sure a pointer was passed */
    if (h != NULL)
    {
        /* Fill in the hook fields */
        h->h_Entry    = func;
        h->h_SubEntry = NULL;
        h->h_Data     = data;
    }
}

The following is a simple example of a callback hook function.

// hooks1.c
 
#include <exec/types.h>
#include <exec/libraries.h>
#include <utility/hooks.h>
#include <dos.h>
 
// Note the newlib startup code opens these libraries.
#include <proto/exec.h>
#include <proto/utility.h>
 
/* This function only prints out a message indicating that we are
 * inside the callback function.
 */
uint32 MyFunction (struct Hook *h, VOID *o, VOID *msg)
{
    /* Debugging function to send a string to the serial port */
    IExec->DebugPrintF("Inside MyFunction()\n");
 
    return (1);
}
 
int main()
{
    struct Hook *h = IExec->AllocSysObjectTags(ASOT_HOOK,
      ASOHOOK_Entry, MyFunction,
      TAG_END);
 
    if (h != NULL)
    {
        /* Use the utility library function to invoke the Hook */
        IUtility->CallHookPkt (h, NULL, NULL);
 
        IExec->FreeSysObject(ASOT_HOOK, h);
    }
}