/********************************************************************************/
/*
* File name: cameraevents.cpp
* Location:  ...\Matrox Imaging\MILxxx\Examples\BoardSpecific\USB3Vision\C++\cameraevents
 *             
*
* Synopsis:  This program demonstrates new MIL features for managing GenICam(c)
*            devices. This program focuses on hooking a MIL handler to asynchronous
*            camera events.
*
*/

/* Headers. */
#include <mil.h>
#if M_MIL_USE_WINDOWS
#include <windows.h>
#endif

#if M_MIL_UNICODE_API
#define MosStrncmp   wcsncmp
#else
#define MosStrncmp   strncmp
#endif

typedef struct
   {
   MIL_ID  MilDigitizer;
   MIL_ID  MilImageDisp;
   MIL_INT ProcessedImageCount;
   MIL_TEXT_PTR* Events;
   MIL_INT EventCount;
   MIL_DOUBLE TimeStamp;
   } HookDataStruct;

/* Gets and prints the events supported by the camera. */
void GetCameraEventControls(MIL_ID MilDigitizer, MIL_INT& SupportedEventCount,
                            MIL_TEXT_PTR** Events, bool& StandardEventEnable);
/* Hooks a MIL function callback to camera events. */
void HookToEvent(MIL_ID MilDigitizer, MIL_TEXT_PTR Event, void* HookDataPtr,
                 bool StandardEventEnable, MIL_INT Unhook = 0);
/* Does a lookup of the event name that triggered the callback. */
void EventTypeLookUp(MIL_ID MilDigitizer, MIL_ID HookId, HookDataStruct* HookDataPtr);
/* Starts image acquisition. */
void DoAcquisition(MIL_ID MilSystem, MIL_ID MilDigitizer, MIL_ID MilImageDisp);

/* User's processing function prototype. */
MIL_INT MFTYPE ProcessingFunction(MIL_INT HookType,
                                  MIL_ID HookId,
                                  void* HookDataPtr);

/* User's callback function prototype. Called when a camera event fires. */
MIL_INT MFTYPE CameraEventHandler(MIL_INT HookType,
                                  MIL_ID HookId,
                                  void* HookDataPtr);


/* Main function. */
int MosMain(void)
   {
   MIL_ID MilApplication,  /* Application identifier.  */
          MilSystem,       /* System identifier.       */
          MilDisplay,      /* Display identifier.      */
          MilDigitizer,    /* Digitizer identifier.    */
          MilImage;        /* Image buffer identifier. */
   MIL_INT BoardType, EventCount, Done = 0;
   MIL_TEXT_PTR* Events;
   MIL_INT Selection = 0;
   bool StandardEventEnbale = false;
   HookDataStruct UserHookData;

   /* Allocate defaults. */
   MappAllocDefault(M_DEFAULT, &MilApplication, &MilSystem, &MilDisplay, &MilDigitizer, M_NULL);

   MbufAllocColor(MilSystem,
                  MdigInquire(MilDigitizer, M_SIZE_BAND, M_NULL),
                  MdigInquire(MilDigitizer, M_SIZE_X, M_NULL),
                  MdigInquire(MilDigitizer, M_SIZE_Y, M_NULL),
                  MdigInquire(MilDigitizer, M_TYPE, M_NULL),
                  M_IMAGE+M_DISP+M_GRAB,
                  &MilImage);

   MosPrintf(MIL_TEXT("------------------------------------------------------------\n\n"));

   /* Get information on the system we are using and print a welcome message to the console. */
   MsysInquire(MilSystem, M_BOARD_TYPE, &BoardType);

   if(((BoardType & M_BOARD_TYPE_MASK) == M_GIGE_VISION) || ((BoardType & M_BOARD_TYPE_MASK) == M_USB3_VISION))
      MosPrintf(MIL_TEXT("Matrox Driver for GigE/USB3 Vision - Camera event example\n\n"));
   else
      {
      MosPrintf(MIL_TEXT("This example program can only be used with the Matrox Driver for ")
                MIL_TEXT("GigE Vision or USB3 Vision.\n"));
      MosPrintf(MIL_TEXT("Please ensure that the default system type is set accordingly in ")
                MIL_TEXT("MIL Config.\n"));
      MosPrintf(MIL_TEXT("-------------------------------------------------------------\n\n"));
      MosPrintf(MIL_TEXT("Press <enter> to quit.\n"));
      MosGetch();
      MappFreeDefault(MilApplication, MilSystem, MilDisplay, MilDigitizer, MilImage);
      return 1;
      }

   MosPrintf(MIL_TEXT("Press <Enter> to start.\n\n"));
   MosGetch();

   /* Disable error printing in case camera is not
      Standard Feature Naming Convention compliant. */
   MappControl(M_DEFAULT, M_ERROR, M_PRINT_DISABLE);

   /*Get the list of events supported by the camera. */
   GetCameraEventControls(MilDigitizer, EventCount, &Events, StandardEventEnbale);

   /* Re-enable error printing. */
   MappControl(M_DEFAULT, M_ERROR, M_PRINT_ENABLE);

   /* If camera supports events, ask the user to select an event to use. */
   if(EventCount)
      {
      do
         {
         MosPrintf(MIL_TEXT("\nPlease select the event you wish to hook a function to: "));
#if M_MIL_USE_WINDOWS
         scanf_s("%ld", &Selection);
#else
         scanf("%ld", &Selection);
#endif
         if((Selection >= 0) && (Selection < EventCount))
            Done = 1;
         else
            MosPrintf(MIL_TEXT("\nInvalid selection"));
         }
      while(!Done);

      UserHookData.MilDigitizer = MilDigitizer;
      UserHookData.MilImageDisp = MilImage;
      UserHookData.ProcessedImageCount = 0;
      UserHookData.Events = Events;
      UserHookData.EventCount = EventCount;
      UserHookData.TimeStamp = 0.0;

      /* Hook a callback to the camera's event. */
      HookToEvent(MilDigitizer, Events[Selection], &UserHookData, StandardEventEnbale);
      
      MosPrintf(MIL_TEXT("\nAwaiting %s events.\n"), Events[Selection]);
      MosPrintf(MIL_TEXT("Press <Enter> to continue.\n"));
      MosGetch();

      /* Start a continuous acquisition. */
      MdispSelect(MilDisplay, MilImage);

      /* Some events such as ExposureStart, ExposureEnd, ..., require an acquisition
         in order to be generated, therefore start an acquisition. */
      MosPrintf(MIL_TEXT("\nPress <Enter> to quit.\n"));
      DoAcquisition(MilSystem, MilDigitizer, MilImage);
      MosGetch();

      /* Unhook MIL callback from event. */
      HookToEvent(MilDigitizer, Events[Selection], &UserHookData, StandardEventEnbale, M_UNHOOK);
      }

   /* Free allocations. */
   if(EventCount)
      {
      for(MIL_INT i = 0; i < EventCount; i++)
         delete [] Events[i];

      delete[] Events;
      }

   MappFreeDefault(MilApplication, MilSystem, MilDisplay, MilDigitizer, MilImage);

   return 0;
   }

/* Gets and prints the events supported by the camera. */
void GetCameraEventControls(MIL_ID MilDigitizer, MIL_INT& SupportedEventCount,
                            MIL_TEXT_PTR** Events, bool& StandardEventEnable)
   {
   MIL_TEXT_PTR CameraVendor = M_NULL;
   MIL_TEXT_PTR CameraModel = M_NULL;
   MIL_TEXT_PTR* EventNotification = M_NULL;
   MIL_INT EventCnt = 0, Cnt = 0, Len = 0;
   bool SupportsOn = false, SupportsOff = false;

   StandardEventEnable = false;

   /* Inquire general device information such as device vendor and name. */
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE+M_STRING_SIZE, MIL_TEXT("DeviceVendorName"), M_DEFAULT, &Len);
   if(Len)
      {
      CameraVendor = new MIL_TEXT_CHAR[Len];
      MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("DeviceVendorName"), M_TYPE_STRING, CameraVendor);
      }

   Len = 0;
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE+M_STRING_SIZE, MIL_TEXT("DeviceModelName"), M_DEFAULT, &Len);
   if(Len)
      {
      CameraModel = new MIL_TEXT_CHAR[Len];
      MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("DeviceModelName"), M_TYPE_STRING, CameraModel);
      }

   /* Inquire supported events. */
   MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_COUNT, MIL_TEXT("EventSelector"), M_DEFAULT, &EventCnt);
   if(EventCnt)
      {
      (*Events) = new MIL_TEXT_PTR [EventCnt];
      for(MIL_INT i=0; i<EventCnt; i++)
         {
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+M_STRING_SIZE+i, MIL_TEXT("EventSelector"), M_DEFAULT, &Len);
         (*Events)[i] = new MIL_TEXT_CHAR[Len];
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+i, MIL_TEXT("EventSelector"), M_DEFAULT, (*Events)[i]);
         }
      }
   
   /*Validate if device supports standard event notification mechanism. */
   MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_COUNT, MIL_TEXT("EventNotification"), M_DEFAULT, &Cnt);
   if(Cnt)
      {
      EventNotification = new MIL_TEXT_PTR[Cnt];
      for(MIL_INT i=0; i<Cnt; i++)
         {
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+M_STRING_SIZE+i, MIL_TEXT("EventNotification"), M_DEFAULT, &Len);
         EventNotification[i] = new MIL_TEXT_CHAR[Len];
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+i, MIL_TEXT("EventNotification"), M_DEFAULT, EventNotification[i]);

         if(MosStrcmp(EventNotification[i], MIL_TEXT("On")) == 0)
            SupportsOn = true;
         else if(MosStrcmp(EventNotification[i], MIL_TEXT("Off")) == 0)
            SupportsOff = true;
         }

      if(SupportsOn && SupportsOff)
         StandardEventEnable = true;
      }

   /* Print data inquired above. */
   MosPrintf(MIL_TEXT("%20s %s %s\n"), MIL_TEXT("Connected to camera:"),
             CameraVendor?CameraVendor:MIL_TEXT("N/A"), CameraModel?CameraModel:MIL_TEXT("N/A"));
   MosPrintf(MIL_TEXT("%20s "), MIL_TEXT("Supported events:"));

   /* If camera supports events, ask the user to which event to hook to. */
   if(EventCnt == 0)
      {
      MosPrintf(MIL_TEXT("Your camera does not support events.\n\n"));
      MosPrintf(MIL_TEXT("Press <Enter> to quit.\n\n"));
      MosGetch();
      }
   else
      {
      MosPrintf(MIL_TEXT("(0) %s\n"), (*Events)[0]);
      for(MIL_INT i = 1; i < EventCnt; i++)
         MosPrintf(MIL_TEXT("%20s (%d) %s\n"), MIL_TEXT(""), i, (*Events)[i]);
      }

   /* Free allocations. */
   if(Cnt)
      {
      for(MIL_INT i=0; i<Cnt; i++)
         delete [] EventNotification[i];

      delete [] EventNotification;
      }

   if(CameraVendor)
      delete [] CameraVendor;
   if(CameraModel)
      delete [] CameraModel;

   SupportedEventCount = EventCnt;
   }

/* Hooks a MIL function callback to camera events. */
void HookToEvent(MIL_ID MilDigitizer, MIL_TEXT_PTR Event, void* HookDataPtr,
                 bool StandardEventEnable, MIL_INT Unhook /*= 0*/)
   {
   MIL_INT MilHookType = 0;
   MIL_INT Error = M_NULL_ERROR;
   bool UnknownEventType = false;
   if(Unhook)
      Unhook = M_UNHOOK;

   if(StandardEventEnable)
      {
      /* If the camera supports standard event notification we can simply hook user MdigHookFunction with the event name. */
      if(MosStrcmp(Event, MIL_TEXT("AcquisitionTrigger")) == 0)
         {
         MdigHookFunction(MilDigitizer, M_GC_EVENT+M_ACQUISITION_TRIGGER+Unhook, CameraEventHandler, HookDataPtr);
         }
      else if(MosStrcmp(Event, MIL_TEXT("AcquisitionStart")) == 0)
         {
         MdigHookFunction(MilDigitizer, M_GC_EVENT+M_ACQUISITION_START+Unhook, CameraEventHandler, HookDataPtr);
         }
      else if(MosStrcmp(Event, MIL_TEXT("AcquisitionEnd")) == 0)
         {
         MdigHookFunction(MilDigitizer, M_GC_EVENT+M_ACQUISITION_END+Unhook, CameraEventHandler, HookDataPtr);
         }
      else if(MosStrcmp(Event, MIL_TEXT("AcquisitionTransferStart")) == 0)
         {
         MdigHookFunction(MilDigitizer, M_GC_EVENT+M_ACQUISITION_TRANSFER_START+Unhook, CameraEventHandler, HookDataPtr);
         }
      else if(MosStrcmp(Event, MIL_TEXT("AcquisitionTransferEnd")) == 0)
         {
         MdigHookFunction(MilDigitizer, M_GC_EVENT+M_ACQUISITION_TRANSFER_END+Unhook, CameraEventHandler, HookDataPtr);
         }
      else if(MosStrcmp(Event, MIL_TEXT("AcquisitionError")) == 0)
         {
         MdigHookFunction(MilDigitizer, M_GC_EVENT+M_ACQUISITION_ERROR+Unhook, CameraEventHandler, HookDataPtr);
         }
      else if(MosStrcmp(Event, MIL_TEXT("FrameTrigger")) == 0)
         {
         MdigHookFunction(MilDigitizer, M_GC_EVENT+M_FRAME_TRIGGER+Unhook, CameraEventHandler, HookDataPtr);
         }
      else if(MosStrcmp(Event, MIL_TEXT("FrameStart")) == 0)
         {
         MdigHookFunction(MilDigitizer, M_GC_EVENT+M_FRAME_START+Unhook, CameraEventHandler, HookDataPtr);
         }
      else if(MosStrcmp(Event, MIL_TEXT("FrameEnd")) == 0)
         {
         MdigHookFunction(MilDigitizer, M_GC_EVENT+M_FRAME_END+Unhook, CameraEventHandler, HookDataPtr);
         }
      else if(MosStrcmp(Event, MIL_TEXT("FrameTransferStart")) == 0)
         {
         MdigHookFunction(MilDigitizer, M_GC_EVENT+M_FRAME_TRANSFER_START+Unhook, CameraEventHandler, HookDataPtr);
         }
      else if(MosStrcmp(Event, MIL_TEXT("FrameTransferEnd")) == 0)
         {
         MdigHookFunction(MilDigitizer, M_GC_EVENT+M_FRAME_TRANSFER_END+Unhook, CameraEventHandler, HookDataPtr);
         }
      else if(MosStrcmp(Event, MIL_TEXT("ExposureStart")) == 0)
         {
         MdigHookFunction(MilDigitizer, M_GC_EVENT+M_EXPOSURE_START+Unhook, CameraEventHandler, HookDataPtr);
         }
      else if(MosStrcmp(Event, MIL_TEXT("ExposureEnd")) == 0)
         {
         MdigHookFunction(MilDigitizer, M_GC_EVENT+M_EXPOSURE_END+Unhook, CameraEventHandler, HookDataPtr);
         }
      else if(MosStrncmp(Event, MIL_TEXT("Counter"), MosStrlen(MIL_TEXT("Counter"))) == 0)
         {
         /* For counter events we must generate the string that targets the proper counter instance. */
         MIL_TEXT_CHAR String1[256];
         MIL_TEXT_CHAR String2[256];
         MIL_INT Count = 0;
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_COUNT, MIL_TEXT("CounterSelector"), M_DEFAULT, &Count);
         for(int i=0; i<Count && MilHookType == 0; i++)
            {
            MosSprintf(String1, 256, MIL_TEXT("Counter%dStart"), i);
            MosSprintf(String2, 256, MIL_TEXT("Counter%dEnd"), i);
            
            if(MosStrcmp(Event, String1) == 0)
               MilHookType = M_GC_EVENT+M_COUNTER_START;
            else if(MosStrcmp(Event, String2) == 0)
               MilHookType = M_GC_EVENT+M_COUNTER_END;
            }
         
         if(MilHookType)
            MdigHookFunction(MilDigitizer, MilHookType+Unhook, CameraEventHandler, HookDataPtr);
         else
            UnknownEventType = true;
         }
      else if(MosStrncmp(Event, MIL_TEXT("Timer"), MosStrlen(MIL_TEXT("Timer"))) == 0)
         {
         /* For timer events we must generate the string that targets the proper timer instance. */
         MIL_TEXT_CHAR String1[256];
         MIL_TEXT_CHAR String2[256];
         MIL_INT Count = 0;
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_COUNT, MIL_TEXT("TimerSelector"), M_DEFAULT, &Count);
         for(int i=0; i<Count && MilHookType == 0; i++)
            {
            MosSprintf(String1, 256, MIL_TEXT("Timer%dStart"), i);
            MosSprintf(String2, 256, MIL_TEXT("Timer%dEnd"), i);
            
            if(MosStrcmp(Event, String1) == 0)
               MilHookType = M_GC_EVENT+M_TIMER_START;
            else if(MosStrcmp(Event, String2) == 0)
               MilHookType = M_GC_EVENT+M_TIMER_END;
            }

         if(MilHookType)
            MdigHookFunction(MilDigitizer, MilHookType+Unhook, CameraEventHandler, HookDataPtr);
         else
            UnknownEventType = true;
         }
      else if(MosStrncmp(Event, MIL_TEXT("Line"), MosStrlen(MIL_TEXT("Line"))) == 0)
         {
         /* For Line events we must generate the string that targets the proper Line instance. */
         MIL_TEXT_CHAR String1[256];
         MIL_TEXT_CHAR String2[256];
         MIL_TEXT_CHAR String3[256];
         MIL_INT Count = 0;
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_COUNT, MIL_TEXT("LineSelector"), M_DEFAULT, &Count);
         for(int i=0; i<Count && MilHookType == 0; i++)
            {
            MosSprintf(String1, 256, MIL_TEXT("Line%dRisingEdge"), i);
            MosSprintf(String2, 256, MIL_TEXT("Line%dFallingEdge"), i);
            MosSprintf(String3, 256, MIL_TEXT("Line%dAnyEdge"), i);
            
            if(MosStrcmp(Event, String1) == 0)
               MilHookType = M_GC_EVENT+M_LINE_RISING_EDGE;
            else if(MosStrcmp(Event, String2) == 0)
               MilHookType = M_GC_EVENT+M_LINE_FALLING_EDGE;
            else if(MosStrcmp(Event, String3) == 0)
               MilHookType = M_GC_EVENT+M_LINE_ANY_EDGE;
            }
         
         if(MilHookType)
            MdigHookFunction(MilDigitizer, MilHookType+Unhook, CameraEventHandler, HookDataPtr);
         else
            UnknownEventType = true;
         }
      else
         {
         /* Unknown hook types are handled as generic M_GC_EVENT indicated below. */
         UnknownEventType = true;
         }
      }

   if((StandardEventEnable == false) || UnknownEventType)
      {
      /* Disable error printing for cameras that do not support the SNFC EventNotification feature. */
      MappControl(M_DEFAULT, M_ERROR, M_PRINT_DISABLE);

      //Hook to a generic (unknown to MIL) event type.
      MdigHookFunction(MilDigitizer, M_GC_EVENT+Unhook, CameraEventHandler, HookDataPtr);

      //Try to enable the event assuming that the "EventNotification" feature is implemented in the camera.
      MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("EventSelector"), M_DEFAULT, Event);
      if(!Unhook)
         {
         MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("EventNotification"), M_DEFAULT, MIL_TEXT("On"));
         
         MappGetError(M_DEFAULT, M_CURRENT+M_THREAD_CURRENT, &Error);
         if(Error != M_NULL_ERROR)
            {
            /* Standard EventNotification support is missing. Print message to user about this. */
            MosPrintf(MIL_TEXT("\nThe %s feature as implemented by the camera manufacturer lacks\n"), Event);
            MosPrintf(MIL_TEXT("standard \"EventNotification\" support. Make sure the event is enabled using\n"));
            MosPrintf(MIL_TEXT("the feature browser before continuing.\n\n"));
            MosPrintf(MIL_TEXT("Some older camera models might require \"EventNotification\" to be set to\n"));
            MosPrintf(MIL_TEXT("\"GigEVisionEvent\" or \"GenICamEvent\" for event notification to occur.\n"));

            /* Pop up the feature browser, the user should find the feature that enables event notification and set it. */
            MdigControl(MilDigitizer, M_GC_FEATURE_BROWSER, M_OPEN+M_ASYNCHRONOUS);
            }
         }
      else
         MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("EventNotification"), M_DEFAULT, MIL_TEXT("Off"));
   
      MappControl(M_DEFAULT, M_ERROR, M_PRINT_ENABLE);
      }
   }

/* Does a lookup of the event name that triggered the callback. */
void EventTypeLookUp(MIL_ID MilDigitizer, MIL_ID HookId, HookDataStruct* HookDataPtr)
   {
   MIL_TEXT_CHAR String[256];
   bool Found = false;
   MIL_DOUBLE EventTimeStamp;
   MIL_INT EventType;
   MIL_INT64 LocalEventType = 0;

   /* Inquire the raw event type and the timestamp when the event occurred. */
   MdigGetHookInfo(HookId, M_GC_EVENT_TYPE, &EventType);
   MdigGetHookInfo(HookId, M_GC_CAMERA_TIME_STAMP, &EventTimeStamp);

   MappControl(M_DEFAULT, M_ERROR, M_PRINT_DISABLE);
   for(MIL_INT i=0; i<HookDataPtr->EventCount && !Found; i++)
      {
      /*Lookup the event name from the raw event type. */
      MosSprintf(String, 256, MIL_TEXT("Event%s"), HookDataPtr->Events[i]);
      MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, String, M_TYPE_INT64, &LocalEventType);
      if(LocalEventType == EventType)
         {
         MosPrintf(MIL_TEXT("Received %s event. Interval from last: %.6f sec.\n"),
                   HookDataPtr->Events[i], EventTimeStamp-HookDataPtr->TimeStamp);
         Found = true;
         }
      }
   MappControl(M_DEFAULT, M_ERROR, M_PRINT_ENABLE);

   /* Look-up failed, camera probably does not support EventExposureData features. 
      Simply print the raw event type. */
   if(Found == false)
      MosPrintf(MIL_TEXT("Event #%d received. Interval from last: %.6f sec.\n"),
                EventType, EventTimeStamp-HookDataPtr->TimeStamp);

   HookDataPtr->TimeStamp = EventTimeStamp;
   }

/* User's processing function hook data structure. */
void DoAcquisition(MIL_ID MilSystem, MIL_ID MilDigitizer, MIL_ID MilImageDisp)
   {
   MIL_INT NbFrames = 10;
   MIL_INT MilGrabBufferListSize;
   MIL_INT Done = 0;
   MIL_INT Ch = 0;
   MIL_ID* MilGrabBufferList = NULL;
   HookDataStruct UserHookData;

   MilGrabBufferList = new MIL_INT[(NbFrames==M_INFINITE) ? 10 : NbFrames];

   /* Allocate the grab buffers and clear them. */
   MappControl(M_DEFAULT, M_ERROR, M_PRINT_DISABLE);
   for(MilGrabBufferListSize = 0; 
            MilGrabBufferListSize<NbFrames; MilGrabBufferListSize++)
      {
      MbufAlloc2d(MilSystem,
                  MdigInquire(MilDigitizer, M_SIZE_X, M_NULL),
                  MdigInquire(MilDigitizer, M_SIZE_Y, M_NULL),
                  8+M_UNSIGNED,
                  M_IMAGE+M_GRAB+M_PROC,
                  &MilGrabBufferList[MilGrabBufferListSize]);

      if (MilGrabBufferList[MilGrabBufferListSize])
         {
         MbufClear(MilGrabBufferList[MilGrabBufferListSize], 0xFF);
         }
      else
         break;
      }
   MappControl(M_DEFAULT, M_ERROR, M_PRINT_ENABLE);

      /* Initialize the User's processing function data structure. */
   UserHookData.MilDigitizer        = MilDigitizer;
   UserHookData.MilImageDisp        = MilImageDisp;
   UserHookData.ProcessedImageCount = 0;

   /* Start the processing. The processing function is called for every frame grabbed. */
   MdigProcess(MilDigitizer, MilGrabBufferList, MilGrabBufferListSize,
                             M_START, M_DEFAULT, ProcessingFunction, &UserHookData);

   /* NOTE: Now the main() is free to perform other tasks 
                                                    while the processing is executing. */
   /* -------------------------------------------------------------------------------- */
   MosGetch();

   /* Stop the processing. */
   MdigProcess(MilDigitizer, MilGrabBufferList, MilGrabBufferListSize,
                              Done ? M_STOP : M_STOP+M_WAIT, M_DEFAULT, ProcessingFunction, &UserHookData);
   
   /* Free the grab buffers. */
   while(MilGrabBufferListSize > 0)
      MbufFree(MilGrabBufferList[--MilGrabBufferListSize]);
   
   delete [] MilGrabBufferList;
   }

/* User's processing function called every time a grab buffer is modified. */
/* -----------------------------------------------------------------------*/

/* Local defines. */
#define STRING_LENGTH_MAX  20
#define STRING_POS_X       20
#define STRING_POS_Y       20

MIL_INT MFTYPE ProcessingFunction(MIL_INT HookType,
                                  MIL_ID HookId,
                                  void* HookDataPtr)
   {
   HookDataStruct *UserHookDataPtr = (HookDataStruct *)HookDataPtr;
   MIL_ID ModifiedBufferId;
   MIL_TEXT_CHAR Text[STRING_LENGTH_MAX]= {MIL_TEXT('\0'),};

   /* Retrieve the MIL_ID of the grabbed buffer. */
   MdigGetHookInfo(HookId, M_MODIFIED_BUFFER+M_BUFFER_ID, &ModifiedBufferId);

   /* Print and draw the frame count. */
   UserHookDataPtr->ProcessedImageCount++;
   MosSprintf(Text, STRING_LENGTH_MAX, MIL_TEXT("%ld"), 
                                       UserHookDataPtr->ProcessedImageCount);
   MgraText(M_DEFAULT, ModifiedBufferId, STRING_POS_X, STRING_POS_Y, Text);

   /* Perform the processing and update the display. */
   MbufCopy(ModifiedBufferId, UserHookDataPtr->MilImageDisp);
   
   return 0;
   }

/* User's callback function prototype. Called when a camera event fires. */
MIL_INT MFTYPE CameraEventHandler(MIL_INT HookType,
                                  MIL_ID HookId, 
                                  void* HookDataPtr)
   {
   HookDataStruct *UserHookDataPtr = (HookDataStruct *)HookDataPtr;

   /* Print info related to the camera event that was fired. */
   EventTypeLookUp(UserHookDataPtr->MilDigitizer, HookId, UserHookDataPtr);

   return 0;
   }