Chapter 14. VL Event Handling

The VL provides several ways of handling data stream events, such as completion or failure of data transfer, vertical retrace event, loss of the path to another client, lack of detectable sync, or dropped fields or frames. The method you use depends on the kind of application you're writing:

Topics in this chapter include:

This chapter concludes with an example illustrating a main loop and event loops.

Querying VL Events

General VL event handling routines are summarized in Table 14-1.

Table 14-1. VL Event Handling Routines

Routine

Use

vlGetFD()

Get a file descriptor for a VL server

vlNextEvent()

Gets the next event; blocks until you get the next event from the queue

vlCheckEvent()

Like a nonblocking vlNextEvent(), this call checks to see if you have an event waiting of the type you specify and reads it off the queue without blocking

vlPeekEvent()

Copies the next event from the queue but, unlike vlNextEvent(), does not update the queue, so that you can see the event without processing it

vlSelectEvents()

Selects video events of interest

vlPending()

Queries whether there is an event waiting for the application

vlEventToName()

Gets the character string with the name of the event; for example, to use in messages

vlAddCallback()

Adds a callback; use for VL events

vlRemoveCallback()

Removes a callback for the events specified if the client data matches that supplied when adding the callback

vlRemoveAllCallbacks()

Removes all callbacks for the specified path and events

vlCallCallbacks()

Creates a handler; used when creating a main loop or using a supplied, non-VL main loop

vlRegisterHandler()

Registers an event handler; use for non-VL events

vlRemoveHandler()

Removes an event handler

The event type is an integer. vlEventToName() allows you to get the character string with the name of the event, so that you can use the event name, for example, in messages.

Table 14-2 summarizes VL event masks.

Table 14-2. VL Event Masks

Symbol

Meaning

VLStreamBusyMask

Stream is locked

VLStreamPreemptedMask

Stream was grabbed by another application

VLAdvanceMissedMask

Time was already reached

VLSyncLostMask

Irregular or interrupted signal

VLSequenceLostMask

Field or frame dropped

VLControlChangedMask

A control has changed

VLControlRangeChangedMask

A control range has changed

VLControlPreemptedMask

Control of a node has been preempted, typically by another user setting VL_LOCK on a path that was previously set with VL_SHARE

VLControlAvailableMask

Access is now available

VLTransferCompleteMask

Transfer of field or frame complete

VLTransferFailedMask

Error; transfer terminated; perform cleanup at this point, including vlEndTransfer()

VLEvenVerticalRetraceMask

Vertical retrace event, even field

VLOddVerticalRetraceMask

Vertical retrace event, odd field

VLFrameVerticalRetraceMask

Frame vertical retrace event

VLDeviceEventMask

Device-specific event, such as a timing change on a Galileo Video node

VLDefaultSourceMask

Default source changed

Call vlGetFD() to get a file descriptor usable from select(2) or poll(2).

Call vlSelectEvents() to express interest in one or more event. For example:

vlSelectEvents(svr, path, VLTransferCompleteMask); 

Event masks can be or'ed together. For example:

vlSelectEvents(svr, path, VLTransferCompleteMask |
               VLTransferFailedMask);

Depending on whether you want to block processing or not, use vlNextEvent() (blocking) or vlCheckEvent() (nonblocking) to get the next event.

Use vlPeekEvent() to see what the next event in the queue is without removing it from the queue. For example, the part of the code that actually gets the event from the event loop uses vlNextEvent(), whereas another part of the code that just wants to know about it, for example, for priority purposes, uses vlPeekEvent().

Creating a VL Event Loop

You can set an event loop to run until a specific condition is fulfilled. The routine vlSelectEvents() allows you to specify which event the application will receive.

Using an event loop requires creating an event mask to specify the events you want. The VL event mask symbols are combined with the bitwise OR operator. For example, to set an event mask to express interest in either transfer complete or control changed events, use:

VLTransferCompleteMask | VLControlChangedMask

To create an event loop, follow these steps:

  1. Define the event; for example:

    VLEvent ev;
    …
    

  2. Set the event mask; for example:

    vlSelectEvents(vlServer, path, VLTransferCompleteMask | VLControlChangedMask)
    

  3. Block on the transfer process until at least one event is waiting:

    for(;;){
    vlNextEvent(vlServer, &ev);
    

  4. Create the loop and define the choices; for example:

    switch(ev.reason){
            case VLTransferComplete:
            …
            break;
        case VLControlChanged:
            …
            break;
        }
    }
    

Creating a Main Loop with Callbacks

vlMainLoop() is provided as a convenience routine for the application programmer and constitutes the main loop of VL applications. This routine first reads the next incoming video event; it then dispatches the event to the appropriate registered procedure. Note that the application does not return from this call.

Applications are expected to exit in response to some user action. There is nothing special about vlMainLoop(); it is simply an infinite loop that calls the next event and then dispatches it. An application can provide its own version of this loop, for example, to test a global termination flag or to test that the number of top-level widgets is larger than zero before circling back to the call to the next event.

To specify callbacks, that is, routines which are called when a particular VL event arrives, use vlAddCallback(). Its function prototype is:

int vlAddCallback(VLServer vlServer, VLEvent * event,
                  void * clientdata, VLEventMask events,
                  VLCallbackProc callback, void *clientData)

Example 14-1 illustrates the use of vlAddCallback().

Example 14-1. Using VL Callbacks

main()
{
  …
      /* Set up the mask for control changed events and Stream preempted events */
   if (vlSelectEvents(vlSvr, vlPath, VLTransferComplete | VLStreamPreemptedMask))
         doErrorExit(“select events”);

   /* Set ProcessEvent() as the callback for VL events */
   vlAddCallback(vlSvr, vlPath, VLTransferCompleteMask | VLStreamPreemptedMask,
                 ProcessEvent, NULL);
 
   /* Start the data transfer immediately (i.e. don't wait for trigger) */
   if (vlBeginTransfer(vlSvr, vlPath, 0, NULL))
        doErrorExit(“begin transfer”);
        
   /* Get and dispatch events */
   vlMainLoop();
}
 
/* Handle VL events */
void
ProcessEvent(VLServer svr, VLEvent *ev, void *data)
{ 
   switch (ev->reason)
   {
      case VLTransferComplete:
        /* Get the valid video data from that frame */
           dataPtr = vlGetActiveRegion(vlSvr, transferBuf, info);
       /* Done with that frame, free the memory used by it */
             vlPutFree(vlSvr, transferBuf);
             frameCount++;
    break;
 
    case VLStreamPreempted:
        fprintf(stderr, “%s: Stream was preempted by another Program\n”,
         _progname);
          docleanup(1);
    break;
 
    default:
    break;
   }
}

Delete a callback with vlRemoveCallback() or vlRemoveAllCallbacks(). Their function prototypes are:

int vlRemoveCallback(VLServer vlServer, VLPath * path,
       VLEventMask events, VLCallbackProc callback, void *clientData)
int vlRemoveAllCallbacks(VLServer vlServer, VLPath * path, VLEventMask events)

The functions vlAddHandler() and vlRemoveHandler() are analogous to vlAddCallback() and vlRemoveCallback(), respectively. Use them for non-VL events.

Example 14-2 illustrates how to create a main loop and event loops.


Caution: To simplify the code, this example does not check returns. The programmer should, however, always check returns.


Example 14-2. VL Event Handling: eventex.c

/*================An Event Driven Application==========
 *
 *
 * File:          eventex.c
 *
 * Usage:         eventex 
 *
 * Description:   event demonstrates VL eventloop with the IRIS GL
 *
 * Functions:     IRIS Video Library functions used
 *
 *                vlOpenVideo()
 *                vlGetNode()
 *                vlCreatePath()
 *                vlSetupPaths()
 *                vlSetControl()
 *                vlCreateBuffer()
 *                vlRegisterBuffer()
 *                vlRegisterHandler()
 *                vlAddCallback()
 *                vlSelectEvents()
 *                vlMainLoop()
 *                vlGetActiveRegion()
 *                vlGetNextValid()
 *                vlPutFree()
 *                vlBeginTransfer()
 *                vlEndTransfer()
 *                vlDeregisterBuffer()
 *                vlDestroyPath()
 *                vlDestroyBuffer()
 *                vlCloseVideo()
 *                vlPerror()
 */
#include <stdlib.h>
#include <stdio.h>
#include <gl/gl.h>
#include <gl/device.h>
#include <dmedia/vl.h>

/*
 *  Function Prototypes
 */
void error_exit(void);
void ProcessEvent(VLServer svr, VLEvent *ev, void *data);
void ProcessGlEvent(int fd, void *win);
void exit_capture(void);

/*
 * Global Variables
 */
char *_progName;
VLBuffer buffer;
VLServer svr;
VLPath path;
VLNode src, drn;
int xsize;
int ysize;

/* Report errors */
void
error_exit(void)
{
    vlPerror(_progName);
    exit(1);
}
void
main(int argc, char **argv)
{
    VLControlValue val;
    int c;
    long win;
    
    _progName = argv[0];
    
    foreground();
        
    /* Connect to the daemon */
    if (!(svr = vlOpenVideo(""))) 
        error_exit();

    /* Set up a drain node in memory */
    drn = vlGetNode(svr, VL_DRN, VL_MEM, VL_ANY);
    
    /* Set up a source node on any video source  */
    src = vlGetNode(svr, VL_SRC, VL_VIDEO, VL_ANY);

    /* Create a path using the first device that will support it */
    path = vlCreatePath(svr, VL_ANY, src, drn); 

    /* Set up the hardware for and define the usage of the path */
    if ((vlSetupPaths(svr, (VLPathList)&path, 1, VL_SHARE,
        VL_SHARE)) < 0)
        error_exit();

    /* Set the packing to RGB */
    val.intVal = VL_PACKING_RGB_8;
    vlSetControl(svr, path, drn, VL_PACKING, &val);
    
    /* Get the video size */
    vlGetControl(svr, path, drn, VL_SIZE, &val);
    xsize = val.xyVal.x;
    ysize = val.xyVal.y;
    
    /* Set up and open a GL window to display the data */
    prefsize(xsize,ysize);
    win = winopen("Eventex Window");
    RGBmode();
    pixmode(PM_TTOB, 1);
    gconfig();
    /* 
     * Allow these key presses, mouseclicks, etc to be 
     * entered in the event queue 
     */
    qdevice(ESCKEY);
    qdevice(WINSHUT);
    qdevice(WINQUIT);
    
    /* Create and register a buffer for 1 frame */
    buffer = vlCreateBuffer(svr, path, drn, 1);
    if (buffer == NULL)
        error_exit();   
    vlRegisterBuffer(svr, path, drn, buffer);
    
    /* Begin the data transfer */
    if (vlBeginTransfer(svr, path, 0, NULL))
        error_exit();
    
    /* 
     * Specify what path-related events we want to receive.
     * In this example we only want transfer complete events.
     */
    vlSelectEvents(svr, path, VLTransferCompleteMask);
    
    /* Set ProcessEvent() is the callback for transfer complete */
    vlAddCallback(svr, path, VLTransferCompleteMask, ProcessEvent,
                  NULL);
    
    /* Set ProcessGlEvent() as the GL event handler */
    vlRegisterHandler(svr, qgetfd(), (VLEventHandler)ProcessGlEvent,
                      (VLPendingFunc) qtest, (void *)win);
                  
    /* Loop and dispatch events */
    vlMainLoop();    
}
/* Handle video library events */
void ProcessEvent(VLServer svr, VLEvent *ev, void *data)
{
    VLInfoPtr info;
    char *dataPtr;

    switch (ev->reason)
    {
        case VLTransferComplete:
            info = vlGetNextValid(svr, buffer);
            if(!info)
                break;
        
            /* Get a pointer to the frame */
            dataPtr = vlGetActiveRegion(svr, buffer, info);
                    
            /* Write the data to the screen */
            lrectwrite(0,0, xsize-1, ysize-1, (ulong *)dataPtr);
        
            /* Finished with frame, unlock the buffer */
            vlPutFree(svr, buffer);
        break;
    
        default:
            printf("Got Event %d\n", ev->reason);
        break;
    }
}
/* Handle graphics library events */
void ProcessGlEvent(int fd, void *win)
{
    static short val;

    switch (qread(&val))
    {
        /* Quit */
        case ESCKEY:
            if (val == 1) /* Respond to keydowns only */
                exit_capture();
        break;
                
        case WINSHUT:
        case WINQUIT:
            exit_capture();
        break;
        
        default:
        break;
    }
}

void
exit_capture()
{
    /* End the data transfer */
    vlEndTransfer(svr, path);
    
    /* Disassociate the ring buffer from the path */
    vlDeregisterBuffer(svr, path, drn, buffer);
    
    /* Destroy the path, free the memory it used */
    vlDestroyPath(svr,path);    
    
    /* Destroy the ring buffer, free the memory it used */
    vlDestroyBuffer(svr, buffer);
    
    /* Disconnect from the daemon */
    vlCloseVideo(svr);
    
    exit(0);