iMSTK
Interactive Medical Simulation Toolkit
Docs/Extras/Event_System.md
1 # Event System
2 
3 Event systems have various pros and cons. iMSTK has one that it minimally uses internally. With iMSTK events, an object that has base class type EventObject may send and receive events.
4 
5 The "connect" function is used to add a receiver/observer to a sender. There are two ways to observe a sender. Queued or direct.
6 
7 - Direct: When the event is emitted, all direct observers are called immediately.
8  - It happens in sync. Sequentially. Does not return from the event emit until receivers are all called.
9  - It happens on the same thread it was emitted from.
10 - Queued: When the event is emitted, all queued observers receive the event in a message queue.
11  - It happens async. Handled sometime later. The observer must implement the pop and handle of the event.
12  - It can switch threads or even machines.
13 
14 Consider a KeyboardDeviceClient. It may emit a KeyEvent.
15 
16 ```cpp
17 std::shared_ptr<KeyboardDeviceClient> myKeyboardDevice = viewer->getKeyboardDevice();
18 connect<KeyEvent>(myKeyboardDevice, &KeyboardDeviceClient::keyPress,
19 [&](KeyEvent* e)
20 {
21  printf("action %d occured on key %d\n", e->m_keyPressType, e->m_key);
22 });
23 ```
24 
25 This function is called immediately and on the same thread when the event happens.
26 
27 Additionally you may direct connect to the function of an object instead of using a C++ lambda.
28 
29 ```cpp
30 std::shared_ptr<KeyboardDeviceClient> myKeyboardDevice = viewer->getKeyboardDevice();
31 connect<KeyEvent>(myKeyboardDevice,
32  &KeyboardDeviceClient::keyPress,
33  myCustomObject,
34  &MyCustomObject::myCustomFunction);
35 ```
36 
37 Alternatively queue it to another object like so:
38 
39 ```cpp
40 std::shared_ptr<KeyboardDeviceClient> myKeyboardDevice = viewer->getKeyboardDevice();
41 queueConnect<KeyEvent>(myKeyboardDevice, &KeyboardDeviceClient::keyPress, sceneManager,
42 [&](KeyEvent* e)
43 {
44  printf("action %d occured on key %d\n", e->m_keyPressType, e->m_key);
45 });
46 ```
47 
48 In this case, the function is not run until sceneManager processes its event queue. SceneManager can process its event queue when it wants too.
49 
50 ## Queued Event Example 1
51 
52 Here a device is setup that will emit a button event and stapler is made to listen to it.
53 
54 ```cpp
55 class StaplerObject : public SceneObject
56 {
57 public:
58  void staple(ButtonEvent* e)
59  {
60  // perform staple;
61  }
62 
63  void update() override
64  {
65  doAllEvents();
66  }
67 };
68 
69 void main()
70 {
71  ... Scene Setup ...
72 
73  // Setup default haptics manager
74  std::shared_ptr<DeviceManager> hapticManager = DeviceManagerFactory::makeDeviceManager();
75  std::shared_ptr<DeviceClient> deviceClient = hapticManager->makeDeviceClient();
76 
77  // Setup our stapler
78  auto stapler = std::make_shared<StaplerObject>();
79  scene->addSceneObject(stapler);
80 
81  // Connect hapticClient buttonStateChanged event to stapler's staple slot
82  queueConnect<ButtonEvent>(hapticClient, &HapticDeviceClient::buttonStateChanged,
83  stapler, &StaplerObject::staple);
84 
85  ... Setup SimulationManager and start ...
86 }
87 ```
88 
89 
90 ## Queued Event Example 2
91 
92 Here a custom event is emitted for a tool when the object is touching. Either directly handle the event, or queue it. Queuing is generally safer but not as fast.
93 
94 ```cpp
95 class ToolObject : public SceneObject
96 {
97 public:
98  SIGNAL(ToolObject, isTouching);
99 
100 public:
101  void update() override
102  {
103  if (geometry is touching)
104  this->postEvent(vent(ToolObject::isTouching()));
105  }
106 };
107 
108 void main()
109 {
110  ... Scene Setup ...
111 
112  auto myToolObject = std::make_shared<ToolObject>();
113  scene->addSceneObject(myToolObject);
114 
115  // We could queue it to anything, even another SceneObject, here we queue it
116  // directly to the sceneManager as we know it will handle its events every update
117  queueConnect<Event>(myToolObject, &ToolObject::isTouching, sceneManager,
118  [&](Event* e)
119  {
120  // Do stuff for when it touches
121  });
122 
123  ... Setup SimulationManager and start ...
124 }
125 ```