Skip to content

Commit a440f34

Browse files
committed
Refactor managers to singleton pattern and improve docs
- Make DisplayManager, TrayManager, and WindowManager singletons - Add GetInstance() methods for singleton access - Update usage in examples and C API to use singleton instances - Add thread safety and usage documentation to manager headers - Refactor TrayManager for thread-safe tray icon management - Expand and clarify documentation for window and display events
1 parent c7341fd commit a440f34

File tree

11 files changed

+667
-96
lines changed

11 files changed

+667
-96
lines changed

examples/window_example/main.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ using nativeapi::WindowManager;
1313
using nativeapi::WindowOptions;
1414

1515
int main() {
16-
DisplayManager display_manager = DisplayManager();
17-
TrayManager tray_manager = TrayManager();
18-
WindowManager window_manager = WindowManager();
16+
DisplayManager& display_manager = DisplayManager::GetInstance();
17+
TrayManager& tray_manager = TrayManager::GetInstance();
18+
WindowManager& window_manager = WindowManager::GetInstance();
1919

2020
// Create a new window with options
2121
WindowOptions options = {.title = "Window Example",

src/capi/display_manager_c.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ native_display_t to_native_display(const Display& raw_display) {
6464
return display;
6565
}
6666

67-
DisplayManager g_display_manager = DisplayManager();
67+
DisplayManager& g_display_manager = DisplayManager::GetInstance();
68+
6869

6970
FFI_PLUGIN_EXPORT
7071
native_display_list_t native_display_manager_get_all() {

src/display_manager.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
#include "display_manager.h"
22

3-
namespace nativeapi {}
3+
namespace nativeapi {
4+
5+
DisplayManager& DisplayManager::GetInstance() {
6+
static DisplayManager instance;
7+
return instance;
8+
}
9+
10+
} // namespace nativeapi

src/display_manager.h

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,17 @@ namespace nativeapi {
1414

1515
/**
1616
* Event class for display addition
17+
*
18+
* This event is emitted when a new display is connected to the system.
1719
*/
1820
class DisplayAddedEvent : public TypedEvent<DisplayAddedEvent> {
1921
public:
2022
explicit DisplayAddedEvent(const Display& display) : display_(display) {}
2123

24+
/**
25+
* Get the added display information
26+
* @return Reference to the added display
27+
*/
2228
const Display& GetDisplay() const { return display_; }
2329

2430
private:
@@ -27,11 +33,17 @@ class DisplayAddedEvent : public TypedEvent<DisplayAddedEvent> {
2733

2834
/**
2935
* Event class for display removal
36+
*
37+
* This event is emitted when a display is disconnected from the system.
3038
*/
3139
class DisplayRemovedEvent : public TypedEvent<DisplayRemovedEvent> {
3240
public:
3341
explicit DisplayRemovedEvent(const Display& display) : display_(display) {}
3442

43+
/**
44+
* Get the removed display information
45+
* @return Reference to the removed display
46+
*/
3547
const Display& GetDisplay() const { return display_; }
3648

3749
private:
@@ -40,23 +52,98 @@ class DisplayRemovedEvent : public TypedEvent<DisplayRemovedEvent> {
4052

4153
/**
4254
* DisplayManager is a singleton that manages all displays on the system.
55+
*
56+
* This class provides functionality to:
57+
* - Query all connected displays
58+
* - Get primary display information
59+
* - Monitor display changes (addition/removal)
60+
* - Get cursor position across displays
61+
*
62+
* The DisplayManager uses the singleton pattern to ensure there's only one
63+
* instance managing the display system throughout the application lifecycle.
64+
*
65+
* Thread Safety: This class is not thread-safe. External synchronization
66+
* is required if accessed from multiple threads.
67+
*
68+
* Example usage:
69+
* @code
70+
* DisplayManager& manager = DisplayManager::GetInstance();
71+
* std::vector<Display> displays = manager.GetAll();
72+
* Display primary = manager.GetPrimary();
73+
* @endcode
4374
*/
4475
class DisplayManager : public EventEmitter {
4576
public:
46-
DisplayManager();
77+
/**
78+
* Get the singleton instance of DisplayManager
79+
* @return Reference to the singleton DisplayManager instance
80+
*/
81+
static DisplayManager& GetInstance();
82+
83+
/**
84+
* @brief Destructor for DisplayManager.
85+
*
86+
* Cleans up all resources, stops event monitoring.
87+
* This is automatically called when the application terminates.
88+
*/
4789
virtual ~DisplayManager();
4890

49-
// Get all displays information
91+
/**
92+
* Get all connected displays information
93+
*
94+
* @return Vector containing all Display objects representing connected
95+
* displays. The vector may be empty if no displays are detected.
96+
* @note The returned vector is a snapshot of current displays at the time of
97+
* call
98+
*/
5099
std::vector<Display> GetAll();
51100

52-
// Get the primary display information
101+
/**
102+
* Get the primary display information
103+
*
104+
* The primary display is typically the main screen where the desktop
105+
* environment displays its primary interface elements.
106+
*
107+
* @return Display object representing the primary display
108+
* @throws std::runtime_error if no primary display is available
109+
*/
53110
Display GetPrimary();
54111

55-
// Get the current cursor position
112+
/**
113+
* Get the current cursor position in screen coordinates
114+
*
115+
* The coordinates are relative to the top-left corner of the primary display,
116+
* with positive X extending right and positive Y extending down.
117+
*
118+
* @return Point containing the current cursor coordinates (x, y)
119+
* @note The position is captured at the time of the function call
120+
*/
56121
Point GetCursorPosition();
57122

123+
// Prevent copy construction and assignment to maintain singleton property
124+
DisplayManager(const DisplayManager&) = delete;
125+
DisplayManager& operator=(const DisplayManager&) = delete;
126+
DisplayManager(DisplayManager&&) = delete;
127+
DisplayManager& operator=(DisplayManager&&) = delete;
128+
58129
private:
130+
/**
131+
* @brief Private constructor to enforce singleton pattern.
132+
*
133+
* Initializes the DisplayManager instance and sets up initial state.
134+
*/
135+
DisplayManager();
136+
137+
/**
138+
* Cached list of displays
139+
* Updated when display configuration changes are detected.
140+
*/
59141
std::vector<Display> displays_;
142+
143+
/**
144+
* Static instance holder for singleton pattern
145+
*/
146+
static DisplayManager* instance_;
60147
};
61148

62149
} // namespace nativeapi
Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,100 @@
1-
#include <cstring>
2-
#include <iostream>
3-
#include <string>
1+
#include <memory>
2+
#include <mutex>
43

5-
#include "../../tray.h"
4+
#include "../../tray_icon.h"
65
#include "../../tray_manager.h"
76

8-
// Import GTK headers
9-
#include <gtk/gtk.h>
7+
// Import GTK headers - these may not be available in all build environments
8+
#ifdef __has_include
9+
#if __has_include(<gtk/gtk.h>)
10+
#include <gtk/gtk.h>
11+
#define HAS_GTK 1
12+
#else
13+
#define HAS_GTK 0
14+
#endif
15+
#else
16+
// Fallback for older compilers
17+
#define HAS_GTK 0
18+
#endif
1019

1120
namespace nativeapi {
1221

1322
TrayManager::TrayManager() : next_tray_id_(1) {}
1423

15-
TrayManager::~TrayManager() {}
24+
TrayManager::~TrayManager() {
25+
std::lock_guard<std::mutex> lock(mutex_);
26+
// Clean up all managed tray icons
27+
for (auto& pair : trays_) {
28+
auto tray = pair.second;
29+
if (tray) {
30+
// The TrayIcon destructor will handle cleanup of the GtkStatusIcon
31+
}
32+
}
33+
trays_.clear();
34+
}
35+
36+
bool TrayManager::IsSupported() {
37+
#if HAS_GTK
38+
// Check if GTK is initialized and system tray is supported
39+
return gtk_init_check(nullptr, nullptr);
40+
#else
41+
// If GTK is not available, assume no system tray support
42+
return false;
43+
#endif
44+
}
45+
46+
std::shared_ptr<TrayIcon> TrayManager::Create() {
47+
std::lock_guard<std::mutex> lock(mutex_);
1648

17-
std::shared_ptr<Tray> TrayManager::Create() {
49+
#if HAS_GTK
1850
// Create a new tray using GTK StatusIcon
1951
GtkStatusIcon* status_icon = gtk_status_icon_new();
20-
auto tray = std::make_shared<Tray>((void*)status_icon);
52+
if (!status_icon) {
53+
return nullptr;
54+
}
55+
56+
auto tray = std::make_shared<TrayIcon>((void*)status_icon);
2157
tray->id = next_tray_id_++;
2258
trays_[tray->id] = tray;
59+
2360
return tray;
61+
#else
62+
// GTK not available, return nullptr
63+
return nullptr;
64+
#endif
2465
}
2566

26-
std::shared_ptr<Tray> TrayManager::Get(TrayID id) {
67+
std::shared_ptr<TrayIcon> TrayManager::Get(TrayIconID id) {
68+
std::lock_guard<std::mutex> lock(mutex_);
69+
2770
auto it = trays_.find(id);
2871
if (it != trays_.end()) {
2972
return it->second;
3073
}
3174
return nullptr;
3275
}
3376

34-
std::vector<std::shared_ptr<Tray>> TrayManager::GetAll() {
35-
std::vector<std::shared_ptr<Tray>> trays;
36-
for (auto& tray : trays_) {
37-
trays.push_back(tray.second);
77+
std::vector<std::shared_ptr<TrayIcon>> TrayManager::GetAll() {
78+
std::lock_guard<std::mutex> lock(mutex_);
79+
80+
std::vector<std::shared_ptr<TrayIcon>> trays;
81+
for (const auto& pair : trays_) {
82+
trays.push_back(pair.second);
3883
}
3984
return trays;
4085
}
4186

42-
} // namespace nativeapi
87+
bool TrayManager::Destroy(TrayIconID id) {
88+
std::lock_guard<std::mutex> lock(mutex_);
89+
90+
auto it = trays_.find(id);
91+
if (it != trays_.end()) {
92+
// Remove the tray icon from our container
93+
// The shared_ptr will automatically clean up when the last reference is released
94+
trays_.erase(it);
95+
return true;
96+
}
97+
return false;
98+
}
99+
100+
} // namespace nativeapi

src/platform/macos/tray_manager_macos.mm

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <cstring>
22
#include <iostream>
33
#include <string>
4+
#include <mutex>
45

56
#include "../../tray_icon.h"
67
#include "../../tray_manager.h"
@@ -12,22 +13,38 @@
1213

1314
TrayManager::TrayManager() : next_tray_id_(1) {}
1415

15-
TrayManager::~TrayManager() {}
16+
TrayManager::~TrayManager() {
17+
std::lock_guard<std::mutex> lock(mutex_);
18+
// Clean up all managed tray icons
19+
for (auto& pair : trays_) {
20+
auto tray = pair.second;
21+
if (tray) {
22+
// The TrayIcon destructor will handle cleanup of the NSStatusItem
23+
}
24+
}
25+
trays_.clear();
26+
}
1627

1728
bool TrayManager::IsSupported() {
1829
return true;
1930
}
2031

2132
std::shared_ptr<TrayIcon> TrayManager::Create() {
33+
std::lock_guard<std::mutex> lock(mutex_);
34+
2235
NSStatusBar* status_bar = [NSStatusBar systemStatusBar];
2336
NSStatusItem* status_item = [status_bar statusItemWithLength:NSVariableStatusItemLength];
37+
2438
auto tray = std::make_shared<TrayIcon>((__bridge void*)status_item);
2539
tray->id = next_tray_id_++;
2640
trays_[tray->id] = tray;
41+
2742
return tray;
2843
}
2944

3045
std::shared_ptr<TrayIcon> TrayManager::Get(TrayIconID id) {
46+
std::lock_guard<std::mutex> lock(mutex_);
47+
3148
auto it = trays_.find(id);
3249
if (it != trays_.end()) {
3350
return it->second;
@@ -36,11 +53,26 @@
3653
}
3754

3855
std::vector<std::shared_ptr<TrayIcon>> TrayManager::GetAll() {
56+
std::lock_guard<std::mutex> lock(mutex_);
57+
3958
std::vector<std::shared_ptr<TrayIcon>> trays;
40-
for (auto& tray : trays_) {
41-
trays.push_back(tray.second);
59+
for (const auto& pair : trays_) {
60+
trays.push_back(pair.second);
4261
}
4362
return trays;
4463
}
4564

65+
bool TrayManager::Destroy(TrayIconID id) {
66+
std::lock_guard<std::mutex> lock(mutex_);
67+
68+
auto it = trays_.find(id);
69+
if (it != trays_.end()) {
70+
// Remove the tray icon from our container
71+
// The shared_ptr will automatically clean up when the last reference is released
72+
trays_.erase(it);
73+
return true;
74+
}
75+
return false;
76+
}
77+
4678
} // namespace nativeapi

0 commit comments

Comments
 (0)