Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 182 additions & 0 deletions docs/DISPLAY_EVENT_USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# DisplayManager Event System Usage Examples

This document demonstrates how to use both the legacy DisplayListener interface and the new EventDispatcher-based event system.

## Legacy API (Backward Compatibility)

```cpp
#include "nativeapi.h"
using namespace nativeapi;

// Option 1: Implement DisplayListener interface
class MyDisplayListener : public DisplayListener {
public:
void OnDisplayAdded(const Display& display) override {
std::cout << "Display added: " << display.name << std::endl;
}

void OnDisplayRemoved(const Display& display) override {
std::cout << "Display removed: " << display.name << std::endl;
}
};

void useLegacyAPI() {
DisplayManager displayManager;
MyDisplayListener listener;

// Register listener
displayManager.AddListener(&listener);

// Events will be received via OnDisplayAdded/OnDisplayRemoved

// Don't forget to remove when done
displayManager.RemoveListener(&listener);
}

// Option 2: Use DisplayEventHandler with callbacks
void useLegacyCallbackAPI() {
DisplayManager displayManager;

DisplayEventHandler handler(
[](const Display& display) {
std::cout << "Display added: " << display.name << std::endl;
},
[](const Display& display) {
std::cout << "Display removed: " << display.name << std::endl;
}
);

displayManager.AddListener(&handler);
// ... use the handler
displayManager.RemoveListener(&handler);
}
```

## New Event API (Recommended)

```cpp
#include "nativeapi.h"
using namespace nativeapi;

// Option 1: Use callback functions (simplest)
void useNewEventAPI() {
DisplayManager displayManager;

// Register event listeners with callbacks
auto addedListenerId = displayManager.AddEventListener<DisplayAddedEvent>(
[](const DisplayAddedEvent& event) {
const Display& display = event.GetDisplay();
std::cout << "Display added: " << display.name << std::endl;
});

auto removedListenerId = displayManager.AddEventListener<DisplayRemovedEvent>(
[](const DisplayRemovedEvent& event) {
const Display& display = event.GetDisplay();
std::cout << "Display removed: " << display.name << std::endl;
});

// Events will be dispatched automatically

// Remove listeners when done
displayManager.RemoveEventListener(addedListenerId);
displayManager.RemoveEventListener(removedListenerId);
}

// Option 2: Implement TypedEventListener (for complex logic)
class MyDisplayEventListener : public TypedEventListener<DisplayAddedEvent> {
public:
void OnTypedEvent(const DisplayAddedEvent& event) override {
const Display& display = event.GetDisplay();
std::cout << "New display detected: " << display.name << std::endl;
// Complex logic here...
}
};

void useNewEventAPIWithListener() {
DisplayManager displayManager;
MyDisplayEventListener listener;

auto listenerId = displayManager.AddEventListener<DisplayAddedEvent>(&listener);

// ... use

displayManager.RemoveEventListener(listenerId);
}

// Option 3: Use EventDispatcher directly
void useEventDispatcherDirectly() {
DisplayManager displayManager;
EventDispatcher& dispatcher = displayManager.GetEventDispatcher();

auto listenerId = dispatcher.AddListener<DisplayAddedEvent>(
[](const DisplayAddedEvent& event) {
// Handle event
});

// You can also dispatch custom events if needed
Display customDisplay;
customDisplay.name = "Custom Display";
dispatcher.DispatchSync<DisplayAddedEvent>(customDisplay);

dispatcher.RemoveListener(listenerId);
}
```

## Migration Guide

### From Legacy to New API

```cpp
// OLD WAY
class OldListener : public DisplayListener {
void OnDisplayAdded(const Display& display) override {
handleDisplayAdded(display);
}
void OnDisplayRemoved(const Display& display) override {
handleDisplayRemoved(display);
}
};

DisplayManager manager;
OldListener listener;
manager.AddListener(&listener);

// NEW WAY
DisplayManager manager;
auto addedId = manager.AddEventListener<DisplayAddedEvent>(
[](const DisplayAddedEvent& event) {
handleDisplayAdded(event.GetDisplay());
});
auto removedId = manager.AddEventListener<DisplayRemovedEvent>(
[](const DisplayRemovedEvent& event) {
handleDisplayRemoved(event.GetDisplay());
});
```

### Benefits of New API

1. **Type Safety**: Compile-time checking of event types
2. **Flexibility**: Can use lambdas, function pointers, or listener classes
3. **Advanced Features**: Async dispatch, listener management by ID
4. **Consistency**: Same event system used across the entire library
5. **Extensibility**: Easy to add new event types without changing interfaces

### Backward Compatibility

The legacy DisplayListener interface is fully supported and will continue to work. Both systems can be used simultaneously - events will be dispatched to both old and new listeners.

## Best Practices

1. **Prefer the new event API** for new code
2. **Use callback functions** for simple event handling
3. **Use TypedEventListener classes** for complex event handling logic
4. **Always remove listeners** to prevent memory leaks
5. **Store listener IDs** returned by AddEventListener for later removal
6. **Consider using RAII** to automatically manage listener lifetimes

## Event Types

- `DisplayAddedEvent`: Fired when a display is connected
- `DisplayRemovedEvent`: Fired when a display is disconnected

Both events provide access to the `Display` object via `GetDisplay()` method.
20 changes: 20 additions & 0 deletions src/display_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,26 @@ void DisplayManager::NotifyListeners(
}
}

void DisplayManager::DispatchDisplayAddedEvent(const Display& display) {
// Dispatch through the new event system
event_dispatcher_.DispatchSync<DisplayAddedEvent>(display);

// Also notify old-style listeners for backward compatibility
NotifyListeners([&display](DisplayListener* listener) {
listener->OnDisplayAdded(display);
});
}

void DisplayManager::DispatchDisplayRemovedEvent(const Display& display) {
// Dispatch through the new event system
event_dispatcher_.DispatchSync<DisplayRemovedEvent>(display);

// Also notify old-style listeners for backward compatibility
NotifyListeners([&display](DisplayListener* listener) {
listener->OnDisplayRemoved(display);
});
}

DisplayEventHandler::DisplayEventHandler(
std::function<void(const Display&)> onDisplayAddedCallback,
std::function<void(const Display&)> onDisplayRemovedCallback)
Expand Down
49 changes: 48 additions & 1 deletion src/display_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,33 @@
#include <vector>

#include "display.h"
#include "event.h"
#include "event_dispatcher.h"
#include "geometry.h"

namespace nativeapi {

// Event classes for display changes
class DisplayAddedEvent : public TypedEvent<DisplayAddedEvent> {
public:
explicit DisplayAddedEvent(const Display& display) : display_(display) {}

const Display& GetDisplay() const { return display_; }

private:
Display display_;
};

class DisplayRemovedEvent : public TypedEvent<DisplayRemovedEvent> {
public:
explicit DisplayRemovedEvent(const Display& display) : display_(display) {}

const Display& GetDisplay() const { return display_; }

private:
Display display_;
};

// DisplayListener is an interface that can be implemented by classes that want
// to listen for display events.
class DisplayListener {
Expand Down Expand Up @@ -39,10 +62,34 @@ class DisplayManager {
// Remove a listener from the display manager
void RemoveListener(DisplayListener* listener);

// Event dispatcher methods for the new system
template <typename EventType>
size_t AddEventListener(TypedEventListener<EventType>* listener) {
return event_dispatcher_.AddListener<EventType>(listener);
}

template <typename EventType>
size_t AddEventListener(std::function<void(const EventType&)> callback) {
return event_dispatcher_.AddListener<EventType>(std::move(callback));
}

bool RemoveEventListener(size_t listener_id) {
return event_dispatcher_.RemoveListener(listener_id);
}

// Get the event dispatcher (for advanced usage)
EventDispatcher& GetEventDispatcher() { return event_dispatcher_; }

private:
std::vector<Display> displays_;
std::vector<DisplayListener*> listeners_;
std::vector<DisplayListener*> listeners_; // For backward compatibility
EventDispatcher event_dispatcher_; // New event system

void NotifyListeners(std::function<void(DisplayListener*)> callback);

// New event dispatch methods
void DispatchDisplayAddedEvent(const Display& display);
void DispatchDisplayRemovedEvent(const Display& display);
};

// DisplayEventHandler is an implementation of DisplayListener that uses
Expand Down
20 changes: 10 additions & 10 deletions src/platform/macos/display_manager_macos.mm
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ static Display CreateDisplayFromNSScreen(NSScreen* screen, bool isPrimary) {
// Set position and size using geometry types
display.position = {frame.origin.x, frame.origin.y};
display.size = {frame.size.width, frame.size.height};
display.workArea = {visibleFrame.origin.x, visibleFrame.origin.y,
visibleFrame.size.height, visibleFrame.size.width};
display.workArea = {visibleFrame.origin.x, visibleFrame.origin.y, visibleFrame.size.height,
visibleFrame.size.width};
display.scaleFactor = scaleFactor;
display.isPrimary = isPrimary;

Expand All @@ -56,7 +56,7 @@ static Display CreateDisplayFromNSScreen(NSScreen* screen, bool isPrimary) {
CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode(displayID);
if (displayMode) {
double refreshRate = CGDisplayModeGetRefreshRate(displayMode);
display.refreshRate = refreshRate > 0 ? (int)refreshRate : 60; // Default to 60Hz if unknown
display.refreshRate = refreshRate > 0 ? (int)refreshRate : 60; // Default to 60Hz if unknown
CGDisplayModeRelease(displayMode);
}

Expand All @@ -81,11 +81,13 @@ static Display CreateDisplayFromNSScreen(NSScreen* screen, bool isPrimary) {

// Use display name as model for now
display.model = display.name;
display.serialNumber = ""; // Not easily available without deprecated APIs
display.serialNumber = ""; // Not easily available without deprecated APIs

// Set default values if hardware info couldn't be retrieved
if (display.manufacturer.empty()) display.manufacturer = "Unknown";
if (display.model.empty()) display.model = "Unknown";
if (display.manufacturer.empty())
display.manufacturer = "Unknown";
if (display.model.empty())
display.model = "Unknown";

return display;
}
Expand All @@ -109,8 +111,7 @@ static Display CreateDisplayFromNSScreen(NSScreen* screen, bool isPrimary) {
old_ids.insert(d.id);
for (const auto& d : new_displays) {
if (old_ids.find(d.id) == old_ids.end()) {
NotifyListeners(
[d](DisplayListener* listener) { listener->OnDisplayAdded(d); });
DispatchDisplayAddedEvent(d);
}
}

Expand All @@ -120,8 +121,7 @@ static Display CreateDisplayFromNSScreen(NSScreen* screen, bool isPrimary) {
new_ids.insert(d.id);
for (const auto& d : old_displays) {
if (new_ids.find(d.id) == new_ids.end()) {
NotifyListeners(
[d](DisplayListener* listener) { listener->OnDisplayRemoved(d); });
DispatchDisplayRemovedEvent(d);
}
}

Expand Down