=============
Custom Events
=============

So far, several types of predefined events have been mentioned. It is also possible to create custom events that can be raised and handled by your application. |HPSNOW| processes custom events no differently from predefined events. However, the setup requires an additional step - you must select a channel for your event type. The remainder of the logic is identical to the built-in event handling mechanism, as shown below:

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00700_custom_event.cpp
		   :start-after: //! [custom_event]
		   :end-before: //! [custom_event]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00700_custom_event.cs
		   :start-after: //! [custom_event]
		   :end-before: //! [custom_event]
		   
A handler instance can subscribe to any number of channels. In this case, the event dispatcher informs the ``MyEventHandler`` instance that it needs to listen for events on the channel number corresponding to the ``Object::ClassID`` of our custom event object -- i.e., ``Object::ClassID`` of ``MyCustomEvent`` in this sample. You can choose any channel number you'd like, but we're using the ``Object::ClassID`` as our channel number to listen specifically for events of this type. (If we selected a channel number of, say, 1 we might encounter other types of events mixed into the channel.)

The channel number is passed as a parameter to the ``HPS::EventDispatcher::Subscribe()`` function. Next, the event is instantiated. Lastly, the event dispatcher injects the event into the event queue with a call to ``HPS::EventDispatcher::InjectEvent()``.

Because events are each handled in their own separate thread, it is important that event handlers do not throw exceptions above their scope since there would be no way to catch the exception.

In the next section, we'll discuss setting up a sub-class of ``HPS::Event`` to define our own custom event.


.. _dropping_events:

Dropping Unimportant Events
===========================

Sometimes, events are generated so quickly that they cannot be processed in real time. Often, these quickly generated events are extraneous or unimportant. For instance, if a user moves the mouse across the screen very quickly, we usually don't care about the cursor's intermediate locations, just the most recent position. On these occasions, |HPSNOW| will try to determine whether the event can be ignored based on the event's internal logic.

For custom events, it is possible to write this logic yourself. This is especially important when processing an event that takes a lot of time. The drop attempt takes place automatically when another event enters the queue before the current event is handled.

When multiple events of the same type are queued in rapid succession, |HPSNOW| will call ``HPS::Event::Drop()`` to determine whether each event is extraneous. ``HPS::Event::Drop()`` only gets invoked if you have two or more events next to each other in the same channel (meaning they are stacking up faster than they can be processed).

In this situation, ``HPS::Event::Drop()`` is invoked on the older event (``this`` inside the ``Event::Drop()`` function is the old ``HPS::Event`` object), and the next newer Event will be passed as a parameter to the ``HPS::Event::Drop()`` function. Prior to injecting an Event, you'll need to ensure the Event has any necessary state information that could be used in the logic for the ``Event::Drop()`` function.

If ``HPS::Event::Drop()`` returns ``false`` (meaning the event is important and should not be dropped), then the event stays in the queue and `` HPS::EventHandler::Handle()`` will eventually be called. If ``HPS::Event::Drop()`` returns ``true`` (meaning the event is not important), then the event is removed from the queue and ``HPS::EventHandler::Handle()`` will not be called.

Below is an example of setting up a custom event with drop logic. Here, we're defining a sub-class of ``HPS::Event``. For demonstration purposes, we'll create some arbitrary logic for the ``Drop()`` function:

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00700_custom_event.cpp
		   :start-after: //! [merging_events_example]
		   :end-before: //! [merging_events_example]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00700_custom_event.cs
		   :start-after: //! [merging_events_example]
		   :end-before: //! [merging_events_example]

As shown in the example, C# users should call the base constructor with the object's class ID in order to register the event in the correct channel.

In order to test out the drop behavior, in our main application code we'll create a ``for`` loop that will inject 100 separate events in rapid succession into the Event dispatcher. Some of these events will be injected while a previous event is still being processed, causing |HPSNOW| to call the ``Drop()`` function internally:

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00700_custom_event.cpp
		   :start-after: //! [drop_event]
		   :end-before: //! [drop_event]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00700_custom_event.cs
		   :start-after: //! [drop_event]
		   :end-before: //! [drop_event]
		   
While running this code in debug mode, if you set a single breakpoint in your ``Drop()`` function, you'll likely see 
a handful of calls to ``Drop()`` during the execution of the ``for`` loop.

For more information on custom events, please see the *custom_event* sample in the |HPSNOW| package, which includes a demonstration of how to use the ``HPS::EventNotifier`` class.


Full Source
-----------

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00700_custom_event_full_source.cpp
		   :start-after: //! [custom_event_full_source]
		   :end-before: //! [custom_event_full_source]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00700_custom_event_full_source.cs
		   :start-after: //! [custom_event_full_source]
		   :end-before: //! [custom_event_full_source]
		   