Chapter 4. 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:

This chapter explains

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

Querying VL Events

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

Table 4-1. VL Event Handling Routines

Routine

Use

vlGetFD()

Gets 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(), 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 4-2 summarizes VL event masks.

Table 4-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 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 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 4-1 illustrates the use of vlAddCallback().

Example 4-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.

In /usr/people/4Dgifts/examples/dmedia/video/vl, the example program eventex.c illustrates how to create a main loop and event loops.


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