Skip to content

Commit 6d5ab15

Browse files
Copilotlijy91
andcommitted
Implement generic event handling system for C++
Co-authored-by: lijy91 <3889523+lijy91@users.noreply.github.com>
1 parent 7172f82 commit 6d5ab15

17 files changed

+1360
-0
lines changed

docs/EVENT_SYSTEM.md

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# Generic Event Handling System
2+
3+
## Overview
4+
5+
The Generic Event Handling System provides a type-safe, flexible, and thread-safe mechanism for handling events in C++. It supports both observer patterns and callback-based event handling, making it suitable for various use cases in native API development.
6+
7+
## Key Features
8+
9+
- **Type Safety**: Template-based design ensures compile-time type checking
10+
- **Flexible Patterns**: Supports both observer pattern and callback-based handling
11+
- **Thread Safety**: Safe for multi-threaded environments with proper synchronization
12+
- **Asynchronous Processing**: Built-in support for async event dispatch with background thread
13+
- **Automatic Cleanup**: RAII-based listener management prevents memory leaks
14+
- **Exception Safety**: Handles exceptions in event listeners gracefully
15+
- **Event Timestamping**: All events include creation timestamps
16+
- **Generic Design**: Works with any event type through templates
17+
18+
## Core Components
19+
20+
### Event Base Classes
21+
22+
- `Event`: Base class for all events, provides timestamping and type information
23+
- `TypedEvent<T>`: Template base class that provides automatic type identification
24+
- `EventListener`: Generic event listener interface
25+
- `TypedEventListener<T>`: Type-safe event listener for specific event types
26+
- `CallbackEventListener<T>`: Wrapper for std::function callbacks
27+
28+
### Event Dispatcher
29+
30+
- `EventDispatcher`: Main class that manages event distribution
31+
- Supports both synchronous and asynchronous event dispatch
32+
- Thread-safe listener management
33+
- Background thread for async event processing
34+
35+
### Utility Classes
36+
37+
- `EventListenerGuard`: RAII helper for automatic listener cleanup
38+
- `GetGlobalEventDispatcher()`: Singleton access to global event dispatcher
39+
40+
## Usage Examples
41+
42+
### 1. Define Custom Events
43+
44+
```cpp
45+
#include "event_system.h"
46+
47+
// Define a custom event
48+
class UserLoginEvent : public nativeapi::TypedEvent<UserLoginEvent> {
49+
public:
50+
UserLoginEvent(const std::string& username, bool success)
51+
: username_(username), success_(success) {}
52+
53+
const std::string& GetUsername() const { return username_; }
54+
bool IsSuccess() const { return success_; }
55+
56+
private:
57+
std::string username_;
58+
bool success_;
59+
};
60+
```
61+
62+
### 2. Observer Pattern
63+
64+
```cpp
65+
// Implement an observer
66+
class LoginObserver : public nativeapi::TypedEventListener<UserLoginEvent> {
67+
public:
68+
void OnTypedEvent(const UserLoginEvent& event) override {
69+
std::cout << "User " << event.GetUsername()
70+
<< (event.IsSuccess() ? " logged in" : " login failed") << std::endl;
71+
}
72+
};
73+
74+
// Register the observer
75+
nativeapi::EventDispatcher dispatcher;
76+
LoginObserver observer;
77+
auto listener_id = dispatcher.AddListener<UserLoginEvent>(&observer);
78+
79+
// Dispatch an event
80+
dispatcher.DispatchSync<UserLoginEvent>("john_doe", true);
81+
82+
// Clean up
83+
dispatcher.RemoveListener(listener_id);
84+
```
85+
86+
### 3. Callback Pattern
87+
88+
```cpp
89+
// Register a callback function
90+
auto callback_id = dispatcher.AddListener<UserLoginEvent>(
91+
[](const UserLoginEvent& event) {
92+
// Handle the event
93+
std::cout << "Callback: " << event.GetUsername() << std::endl;
94+
});
95+
96+
// Or use RAII for automatic cleanup
97+
auto guard = nativeapi::AddScopedListener<UserLoginEvent>(dispatcher,
98+
[](const UserLoginEvent& event) {
99+
std::cout << "Scoped callback: " << event.GetUsername() << std::endl;
100+
});
101+
// Listener is automatically removed when guard goes out of scope
102+
```
103+
104+
### 4. Multi-Event Observer
105+
106+
```cpp
107+
// Handle multiple event types with one observer
108+
class MultiEventObserver : public nativeapi::EventListener {
109+
public:
110+
void OnEvent(const nativeapi::Event& event) override {
111+
if (auto login_event = dynamic_cast<const UserLoginEvent*>(&event)) {
112+
HandleLogin(*login_event);
113+
} else if (auto logout_event = dynamic_cast<const UserLogoutEvent*>(&event)) {
114+
HandleLogout(*logout_event);
115+
}
116+
}
117+
118+
private:
119+
void HandleLogin(const UserLoginEvent& event) { /* ... */ }
120+
void HandleLogout(const UserLogoutEvent& event) { /* ... */ }
121+
};
122+
```
123+
124+
### 5. Asynchronous Event Processing
125+
126+
```cpp
127+
// Start background thread for async processing
128+
dispatcher.Start();
129+
130+
// Dispatch events asynchronously
131+
dispatcher.DispatchAsync<UserLoginEvent>("alice", true);
132+
dispatcher.DispatchAsync<UserLoginEvent>("bob", false);
133+
134+
// Events will be processed on background thread
135+
// Stop background processing when done
136+
dispatcher.Stop();
137+
```
138+
139+
### 6. Global Event System
140+
141+
```cpp
142+
// Use the global event dispatcher
143+
auto& global_dispatcher = nativeapi::GetGlobalEventDispatcher();
144+
145+
global_dispatcher.AddListener<UserLoginEvent>([](const UserLoginEvent& event) {
146+
// This listener is globally available
147+
});
148+
```
149+
150+
## Thread Safety
151+
152+
The event system is designed to be thread-safe:
153+
154+
- Listener registration/removal is protected by mutexes
155+
- Event dispatch creates snapshots of listeners to avoid holding locks during dispatch
156+
- Async event processing uses condition variables for efficient waiting
157+
- Exception handling prevents one listener from affecting others
158+
159+
## Performance Considerations
160+
161+
- **Synchronous dispatch**: Direct function calls, minimal overhead
162+
- **Asynchronous dispatch**: Events are queued and processed on background thread
163+
- **Memory management**: Automatic cleanup of callback listeners
164+
- **Exception handling**: Try-catch blocks around listener calls
165+
166+
## Integration with Existing Code
167+
168+
The event system can be integrated with existing components:
169+
170+
```cpp
171+
// Example: Extend DisplayManager to use events
172+
class EventAwareDisplayManager : public DisplayManager {
173+
public:
174+
EventAwareDisplayManager(nativeapi::EventDispatcher& dispatcher)
175+
: dispatcher_(dispatcher) {}
176+
177+
protected:
178+
void OnDisplayAdded(const Display& display) {
179+
// Existing logic...
180+
181+
// Dispatch event
182+
dispatcher_.DispatchAsync<nativeapi::DisplayAddedEvent>(display);
183+
}
184+
185+
private:
186+
nativeapi::EventDispatcher& dispatcher_;
187+
};
188+
```
189+
190+
## Best Practices
191+
192+
1. **Use RAII**: Prefer `EventListenerGuard` or scoped listeners for automatic cleanup
193+
2. **Handle exceptions**: Event listeners should handle their own exceptions
194+
3. **Avoid blocking**: Keep event handlers fast to avoid blocking other listeners
195+
4. **Use appropriate dispatch**: Sync for immediate handling, async for background processing
196+
5. **Clean up**: Remove listeners when they're no longer needed
197+
6. **Type safety**: Prefer `TypedEventListener` over raw `EventListener` when possible
198+
199+
## Common Event Types
200+
201+
The system includes predefined events for common scenarios:
202+
203+
- `DisplayAddedEvent`, `DisplayRemovedEvent`: Display management
204+
- `KeyPressedEvent`, `KeyReleasedEvent`: Keyboard input
205+
- `MouseMovedEvent`, `MouseClickedEvent`: Mouse input
206+
- `WindowCreatedEvent`, `WindowClosedEvent`: Window management
207+
- `ApplicationStartedEvent`, `ApplicationExitingEvent`: Application lifecycle
208+
209+
These can be used directly or serve as examples for creating custom events.

include/nativeapi.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include "../src/app_runner.h"
55
#include "../src/display.h"
66
#include "../src/display_manager.h"
7+
#include "../src/event_system.h"
8+
#include "../src/common_events.h"
79
#include "../src/geometry.h"
810
#include "../src/keyboard_monitor.h"
911
#include "../src/menu.h"

src/callback_test

194 KB
Binary file not shown.

src/callback_test.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#include <iostream>
2+
#include <cassert>
3+
4+
#include "event_system.h"
5+
6+
// Simple test event
7+
class SimpleEvent : public nativeapi::TypedEvent<SimpleEvent> {
8+
public:
9+
explicit SimpleEvent(int value) : value_(value) {}
10+
int GetValue() const { return value_; }
11+
private:
12+
int value_;
13+
};
14+
15+
int main() {
16+
std::cout << "=== Callback Test ===" << std::endl;
17+
18+
nativeapi::EventDispatcher dispatcher;
19+
20+
// Test callback
21+
bool callback_called = false;
22+
int callback_value = 0;
23+
24+
std::cout << "Adding callback listener..." << std::endl;
25+
auto callback_id = dispatcher.AddListener<SimpleEvent>([&](const SimpleEvent& event) {
26+
std::cout << "Callback called with value: " << event.GetValue() << std::endl;
27+
callback_value = event.GetValue();
28+
callback_called = true;
29+
});
30+
31+
std::cout << "Callback ID: " << callback_id << std::endl;
32+
33+
SimpleEvent event(123);
34+
std::cout << "Dispatching event..." << std::endl;
35+
dispatcher.DispatchSync(event);
36+
37+
std::cout << "Callback called: " << callback_called << std::endl;
38+
std::cout << "Callback value: " << callback_value << std::endl;
39+
40+
if (callback_called && callback_value == 123) {
41+
std::cout << "✓ Test passed!" << std::endl;
42+
} else {
43+
std::cout << "✗ Test failed!" << std::endl;
44+
return 1;
45+
}
46+
47+
return 0;
48+
}

0 commit comments

Comments
 (0)