plusui-native-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/Core/CMakeLists.txt +34 -0
  2. package/Core/README.md +29 -0
  3. package/Core/build/win32/x64/ALL_BUILD.vcxproj +185 -0
  4. package/Core/build/win32/x64/ALL_BUILD.vcxproj.filters +8 -0
  5. package/Core/build/win32/x64/CMakeCache.txt +335 -0
  6. package/Core/build/win32/x64/CMakeFiles/11f7f2f432927ec8d1861dc42d4bd679/INSTALL_force.rule +1 -0
  7. package/Core/build/win32/x64/CMakeFiles/11f7f2f432927ec8d1861dc42d4bd679/generate.stamp.rule +1 -0
  8. package/Core/build/win32/x64/CMakeFiles/4.2.3/CMakeCXXCompiler.cmake +104 -0
  9. package/Core/build/win32/x64/CMakeFiles/4.2.3/CMakeDetermineCompilerABI_CXX.bin +0 -0
  10. package/Core/build/win32/x64/CMakeFiles/4.2.3/CMakeRCCompiler.cmake +6 -0
  11. package/Core/build/win32/x64/CMakeFiles/4.2.3/CMakeSystem.cmake +15 -0
  12. package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/CMakeCXXCompilerId.cpp +949 -0
  13. package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/CompilerIdCXX.exe +0 -0
  14. package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/CompilerIdCXX.vcxproj +72 -0
  15. package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CMakeCXXCompilerId.obj +0 -0
  16. package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.exe.recipe +11 -0
  17. package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.command.1.tlog +2 -0
  18. package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.read.1.tlog +4 -0
  19. package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.write.1.tlog +2 -0
  20. package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/Cl.items.tlog +1 -0
  21. package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CompilerIdCXX.lastbuildstate +2 -0
  22. package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.command.1.tlog +2 -0
  23. package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.read.1.tlog +22 -0
  24. package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.secondary.1.tlog +1 -0
  25. package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.write.1.tlog +2 -0
  26. package/Core/build/win32/x64/CMakeFiles/4.2.3/VCTargetsPath/x64/Debug/VCTargetsPath.recipe +11 -0
  27. package/Core/build/win32/x64/CMakeFiles/4.2.3/VCTargetsPath/x64/Debug/VCTargetsPath.tlog/VCTargetsPath.lastbuildstate +2 -0
  28. package/Core/build/win32/x64/CMakeFiles/4.2.3/VCTargetsPath.txt +1 -0
  29. package/Core/build/win32/x64/CMakeFiles/4.2.3/VCTargetsPath.vcxproj +31 -0
  30. package/Core/build/win32/x64/CMakeFiles/CMakeConfigureLog.yaml +2698 -0
  31. package/Core/build/win32/x64/CMakeFiles/InstallScripts.json +7 -0
  32. package/Core/build/win32/x64/CMakeFiles/TargetDirectories.txt +4 -0
  33. package/Core/build/win32/x64/CMakeFiles/cmake.check_cache +1 -0
  34. package/Core/build/win32/x64/CMakeFiles/generate.stamp +1 -0
  35. package/Core/build/win32/x64/CMakeFiles/generate.stamp.depend +34 -0
  36. package/Core/build/win32/x64/CMakeFiles/generate.stamp.list +1 -0
  37. package/Core/build/win32/x64/Capsule.sln +67 -0
  38. package/Core/build/win32/x64/INSTALL.vcxproj +209 -0
  39. package/Core/build/win32/x64/INSTALL.vcxproj.filters +13 -0
  40. package/Core/build/win32/x64/ZERO_CHECK.vcxproj +179 -0
  41. package/Core/build/win32/x64/ZERO_CHECK.vcxproj.filters +13 -0
  42. package/Core/build/win32/x64/capsule.dir/Release/capsule.tlog/CL.command.1.tlog +2 -0
  43. package/Core/build/win32/x64/capsule.dir/Release/capsule.tlog/CustomBuild.command.1.tlog +10 -0
  44. package/Core/build/win32/x64/capsule.dir/Release/capsule.tlog/CustomBuild.read.1.tlog +33 -0
  45. package/Core/build/win32/x64/capsule.dir/Release/capsule.tlog/CustomBuild.write.1.tlog +2 -0
  46. package/Core/build/win32/x64/capsule.dir/Release/capsule.tlog/capsule.lastbuildstate +2 -0
  47. package/Core/build/win32/x64/capsule.dir/Release/capsule.tlog/unsuccessfulbuild +0 -0
  48. package/Core/build/win32/x64/capsule.vcxproj +332 -0
  49. package/Core/build/win32/x64/capsule.vcxproj.filters +25 -0
  50. package/Core/build/win32/x64/cmake_install.cmake +72 -0
  51. package/Core/build/win32/x64/x64/Release/ZERO_CHECK/ZERO_CHECK.recipe +11 -0
  52. package/Core/build/win32/x64/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.command.1.tlog +10 -0
  53. package/Core/build/win32/x64/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.read.1.tlog +34 -0
  54. package/Core/build/win32/x64/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.write.1.tlog +2 -0
  55. package/Core/build/win32/x64/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate +2 -0
  56. package/Core/include/app.hpp +121 -0
  57. package/Core/include/display.hpp +90 -0
  58. package/Core/include/keyboard.hpp +135 -0
  59. package/Core/include/menu.hpp +79 -0
  60. package/Core/include/tray.hpp +81 -0
  61. package/Core/include/window.hpp +106 -0
  62. package/Core/src/app.cpp +311 -0
  63. package/Core/src/display.cpp +424 -0
  64. package/Core/src/tray.cpp +275 -0
  65. package/Core/src/window.cpp +528 -0
  66. package/Core/vendor/webview.h +551 -0
  67. package/dist/index.d.ts +205 -0
  68. package/dist/index.js +198 -0
  69. package/package.json +19 -0
  70. package/src/index.ts +574 -0
@@ -0,0 +1,424 @@
1
+ #include "display.hpp"
2
+ #include <vector>
3
+ #include <algorithm>
4
+
5
+ #ifdef _WIN32
6
+ #include <windows.h>
7
+ #include <winuser.h>
8
+ #pragma comment(lib, "user32.lib")
9
+ #elif defined(__APPLE__)
10
+ #include <Cocoa/Cocoa.h>
11
+ #include <CoreGraphics/CoreGraphics.h>
12
+ #else
13
+ #include <gtk/gtk.h>
14
+ #include <gdk/gdk.h>
15
+ #endif
16
+
17
+ namespace plusui {
18
+
19
+ struct DisplayManager::Impl {
20
+ std::vector<Display> displays;
21
+ std::function<void(const Display&)> onConnectCallback;
22
+ std::function<void(int)> onDisconnectCallback;
23
+ std::function<void(const Display&)> onChangeCallback;
24
+ };
25
+
26
+ DisplayManager::DisplayManager() : pImpl(std::make_unique<Impl>()) {
27
+ refresh();
28
+ }
29
+
30
+ DisplayManager::~DisplayManager() = default;
31
+
32
+ DisplayManager& DisplayManager::instance() {
33
+ static DisplayManager inst;
34
+ return inst;
35
+ }
36
+
37
+ std::vector<Display> DisplayManager::getAllDisplays() {
38
+ #ifdef _WIN32
39
+ pImpl->displays.clear();
40
+
41
+ int i = 0;
42
+ DISPLAY_DEVICE dd = {};
43
+ dd.cb = sizeof(dd);
44
+
45
+ while (EnumDisplayDevices(nullptr, i, &dd, 0)) {
46
+ if (dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) {
47
+ i++;
48
+ continue;
49
+ }
50
+
51
+ Display d;
52
+ d.id = i;
53
+ d.name = dd.DeviceName;
54
+
55
+ DEVMODE dm = {};
56
+ dm.dmSize = sizeof(dm);
57
+ if (EnumDisplaySettings(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm)) {
58
+ d.bounds.x = dm.dmPosition.x;
59
+ d.bounds.y = dm.dmPosition.y;
60
+ d.bounds.width = dm.dmPelsWidth;
61
+ d.bounds.height = dm.dmPelsHeight;
62
+ d.resolution.width = dm.dmPelsWidth;
63
+ d.resolution.height = dm.dmPelsHeight;
64
+ d.currentMode.width = dm.dmPelsWidth;
65
+ d.currentMode.height = dm.dmPelsHeight;
66
+ d.currentMode.refreshRate = dm.dmDisplayFrequency;
67
+ }
68
+
69
+ d.isPrimary = (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE);
70
+
71
+ HMONITOR hMon = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
72
+ MONITORINFO mi = {};
73
+ mi.cbSize = sizeof(mi);
74
+ if (GetMonitorInfo(hMon, &mi)) {
75
+ d.workArea.x = mi.rcWork.left;
76
+ d.workArea.y = mi.rcWork.top;
77
+ d.workArea.width = mi.rcWork.right - mi.rcWork.left;
78
+ d.workArea.height = mi.rcWork.bottom - mi.rcWork.top;
79
+ }
80
+
81
+ d.scaleFactor = 1.0;
82
+ d.isConnected = true;
83
+
84
+ pImpl->displays.push_back(d);
85
+ i++;
86
+ }
87
+
88
+ #elif defined(__APPLE__)
89
+ pImpl->displays.clear();
90
+
91
+ CGDirectDisplayID mainDisplay = CGMainDisplayID();
92
+ CGDisplayCount count;
93
+ CGGetActiveDisplayList(0, nullptr, &count);
94
+
95
+ std::vector<CGDirectDisplayID> displays(count);
96
+ CGGetActiveDisplayList(count, displays.data(), &count);
97
+
98
+ for (uint32_t i = 0; i < count; i++) {
99
+ Display d;
100
+ d.id = displays[i];
101
+
102
+ CGRect bounds = CGDisplayBounds(displays[i]);
103
+ d.bounds.x = (int)bounds.origin.x;
104
+ d.bounds.y = (int)bounds.origin.y;
105
+ d.bounds.width = (int)bounds.size.width;
106
+ d.bounds.height = (int)bounds.size.height;
107
+ d.resolution.width = (int)bounds.size.width;
108
+ d.resolution.height = (int)bounds.size.height;
109
+
110
+ d.currentMode.width = (int)bounds.size.width;
111
+ d.currentMode.height = (int)bounds.size.height;
112
+ d.currentMode.refreshRate = (int)CGDisplayModeGetRefreshRate(CGDisplayCopyDisplayMode(displays[i]));
113
+
114
+ d.isPrimary = (displays[i] == mainDisplay);
115
+ d.isInternal = CGDisplayIsBuiltin(displays[i]);
116
+ d.isConnected = true;
117
+
118
+ NSScreen* screen = [NSScreen screens][i];
119
+ if (screen) {
120
+ NSRect frame = [screen visibleFrame];
121
+ d.workArea.x = (int)frame.origin.x;
122
+ d.workArea.y = (int)(CGDisplayPixelsHigh(CGMainDisplayID()) - frame.origin.y - frame.size.height);
123
+ d.workArea.width = (int)frame.size.width;
124
+ d.workArea.height = (int)frame.size.height;
125
+ }
126
+
127
+ d.scaleFactor = CGDisplayPixelsHigh(mainDisplay) / CGDisplayPixelsHigh(displays[i]);
128
+
129
+ pImpl->displays.push_back(d);
130
+ }
131
+
132
+ #else
133
+ pImpl->displays.clear();
134
+
135
+ gtk_init_check(0, nullptr);
136
+
137
+ GdkDisplay* display = gdk_display_get_default();
138
+ if (!display) return pImpl->displays;
139
+
140
+ int nMonitors = gdk_display_get_n_monitors(display);
141
+
142
+ for (int i = 0; i < nMonitors; i++) {
143
+ GdkMonitor* monitor = gdk_display_get_monitor(display, i);
144
+
145
+ Display d;
146
+ d.id = i;
147
+
148
+ const char* name = gdk_monitor_get_model(monitor);
149
+ d.name = name ? name : "Display " + std::to_string(i);
150
+
151
+ GdkRectangle rect;
152
+ gdk_monitor_get_geometry(monitor, &rect);
153
+ d.bounds.x = rect.x;
154
+ d.bounds.y = rect.y;
155
+ d.bounds.width = rect.width;
156
+ d.bounds.height = rect.height;
157
+ d.resolution.width = rect.width;
158
+ d.resolution.height = rect.height;
159
+
160
+ d.currentMode.width = rect.width;
161
+ d.currentMode.height = rect.height;
162
+ d.currentMode.refreshRate = gdk_monitor_get_refresh_rate(monitor) / 1000;
163
+
164
+ d.isPrimary = (i == 0);
165
+ d.scaleFactor = gdk_monitor_get_scale_factor(monitor);
166
+
167
+ GdkRectangle work;
168
+ gdk_monitor_get_workarea(monitor, &work);
169
+ d.workArea.x = work.x;
170
+ d.workArea.y = work.y;
171
+ d.workArea.width = work.width;
172
+ d.workArea.height = work.height;
173
+
174
+ d.isConnected = gdk_monitor_is_connected(monitor);
175
+
176
+ pImpl->displays.push_back(d);
177
+ }
178
+ #endif
179
+
180
+ return pImpl->displays;
181
+ }
182
+
183
+ Display DisplayManager::getPrimaryDisplay() {
184
+ auto displays = getAllDisplays();
185
+ auto it = std::find_if(displays.begin(), displays.end(),
186
+ [](const Display& d) { return d.isPrimary; });
187
+ return it != displays.end() ? *it : Display{};
188
+ }
189
+
190
+ Display DisplayManager::getDisplayAt(int x, int y) {
191
+ auto displays = getAllDisplays();
192
+ for (const auto& d : displays) {
193
+ if (x >= d.bounds.x && x < d.bounds.x + d.bounds.width &&
194
+ y >= d.bounds.y && y < d.bounds.y + d.bounds.height) {
195
+ return d;
196
+ }
197
+ }
198
+ return getPrimaryDisplay();
199
+ }
200
+
201
+ Display DisplayManager::getDisplayAtCursor() {
202
+ #ifdef _WIN32
203
+ POINT pt;
204
+ GetCursorPos(&pt);
205
+ return getDisplayAt(pt.x, pt.y);
206
+ #elif defined(__APPLE__)
207
+ CGPoint pt = CGEventGetLocation(CGEventCreate(nullptr));
208
+ return getDisplayAt((int)pt.x, (int)pt.y);
209
+ #else
210
+ gtk_init_check(0, nullptr);
211
+ GdkDisplay* display = gdk_display_get_default();
212
+ if (!display) return Display{};
213
+
214
+ GdkSeat* seat = gdk_display_get_default_seat(display);
215
+ if (!seat) return Display{};
216
+
217
+ GdkDevice* pointer = gdk_seat_get_pointer(seat);
218
+ if (!pointer) return Display{};
219
+
220
+ gint x, y;
221
+ gdk_device_get_position(pointer, nullptr, &x, &y);
222
+ return getDisplayAt(x, y);
223
+ #endif
224
+ }
225
+
226
+ Display DisplayManager::getDisplayById(int id) {
227
+ auto displays = getAllDisplays();
228
+ auto it = std::find_if(displays.begin(), displays.end(),
229
+ [id](const Display& d) { return d.id == id; });
230
+ return it != displays.end() ? *it : Display{};
231
+ }
232
+
233
+ void DisplayManager::onDisplayConnected(std::function<void(const Display&)> callback) {
234
+ pImpl->onConnectCallback = callback;
235
+ }
236
+
237
+ void DisplayManager::onDisplayDisconnected(std::function<void(int)> callback) {
238
+ pImpl->onDisconnectCallback = callback;
239
+ }
240
+
241
+ void DisplayManager::onDisplayChanged(std::function<void(const Display&)> callback) {
242
+ pImpl->onChangeCallback = callback;
243
+ }
244
+
245
+ void DisplayManager::refresh() {
246
+ getAllDisplays();
247
+ }
248
+
249
+ bool DisplayManager::setDisplayMode(int displayId, const DisplayMode& mode) {
250
+ #ifdef _WIN32
251
+ DEVMODE dm = {};
252
+ dm.dmSize = sizeof(dm);
253
+ dm.dmPelsWidth = mode.width;
254
+ dm.dmPelsHeight = mode.height;
255
+ dm.dmDisplayFrequency = mode.refreshRate;
256
+ dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
257
+
258
+ return ChangeDisplaySettingsEx(nullptr, &dm, nullptr, CDS_UPDATEREGISTRY, nullptr) == DISP_CHANGE_SUCCESSFUL;
259
+ #else
260
+ (void)displayId;
261
+ (void)mode;
262
+ return false;
263
+ #endif
264
+ }
265
+
266
+ bool DisplayManager::setPosition(int displayId, int x, int y) {
267
+ #ifdef _WIN32
268
+ DEVMODE dm = {};
269
+ dm.dmSize = sizeof(dm);
270
+ if (EnumDisplaySettings(nullptr, ENUM_CURRENT_SETTINGS, &dm)) {
271
+ dm.dmPosition.x = x;
272
+ dm.dmPosition.y = y;
273
+ dm.dmFields = DM_POSITION;
274
+ return ChangeDisplaySettingsEx(nullptr, &dm, nullptr, CDS_UPDATEREGISTRY, nullptr) == DISP_CHANGE_SUCCESSFUL;
275
+ }
276
+ #else
277
+ (void)displayId;
278
+ (void)x;
279
+ (void)y;
280
+ #endif
281
+ return false;
282
+ }
283
+
284
+ bool DisplayManager::turnOff(int displayId) {
285
+ #ifdef _WIN32
286
+ (void)displayId;
287
+ return SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM)2) != 0;
288
+ #else
289
+ (void)displayId;
290
+ return false;
291
+ #endif
292
+ }
293
+
294
+ int Screen::getWidth() {
295
+ #ifdef _WIN32
296
+ return GetSystemMetrics(SM_CXSCREEN);
297
+ #elif defined(__APPLE__)
298
+ return (int)CGDisplayPixelsWide(CGMainDisplayID());
299
+ #else
300
+ gtk_init_check(0, nullptr);
301
+ GdkDisplay* display = gdk_display_get_default();
302
+ if (!display) return 0;
303
+ GdkMonitor* monitor = gdk_display_get_primary_monitor(display);
304
+ if (!monitor) return 0;
305
+ return gdk_monitor_get_width_mm(monitor);
306
+ #endif
307
+ }
308
+
309
+ int Screen::getHeight() {
310
+ #ifdef _WIN32
311
+ return GetSystemMetrics(SM_CYSCREEN);
312
+ #elif defined(__APPLE__)
313
+ return (int)CGDisplayPixelsHigh(CGMainDisplayID());
314
+ #else
315
+ gtk_init_check(0, nullptr);
316
+ GdkDisplay* display = gdk_display_get_default();
317
+ if (!display) return 0;
318
+ GdkMonitor* monitor = gdk_display_get_primary_monitor(display);
319
+ if (!monitor) return 0;
320
+ return gdk_monitor_get_height_mm(monitor);
321
+ #endif
322
+ }
323
+
324
+ int Screen::getWidthMM() {
325
+ #ifdef _WIN32
326
+ HDC hdc = GetDC(nullptr);
327
+ int width = GetDeviceCaps(hdc, HORZSIZE);
328
+ ReleaseDC(nullptr, hdc);
329
+ return width;
330
+ #elif defined(__APPLE__)
331
+ return 0;
332
+ #else
333
+ gtk_init_check(0, nullptr);
334
+ GdkDisplay* display = gdk_display_get_default();
335
+ if (!display) return 0;
336
+ GdkMonitor* monitor = gdk_display_get_primary_monitor(display);
337
+ if (!monitor) return 0;
338
+ return gdk_monitor_get_width_mm(monitor);
339
+ #endif
340
+ }
341
+
342
+ int Screen::getHeightMM() {
343
+ #ifdef _WIN32
344
+ HDC hdc = GetDC(nullptr);
345
+ int height = GetDeviceCaps(hdc, VERTSIZE);
346
+ ReleaseDC(nullptr, hdc);
347
+ return height;
348
+ #elif defined(__APPLE__)
349
+ return 0;
350
+ #else
351
+ gtk_init_check(0, nullptr);
352
+ GdkDisplay* display = gdk_display_get_default();
353
+ if (!display) return 0;
354
+ GdkMonitor* monitor = gdk_display_get_primary_monitor(display);
355
+ if (!monitor) return 0;
356
+ return gdk_monitor_get_height_mm(monitor);
357
+ #endif
358
+ }
359
+
360
+ double Screen::getScaleFactor() {
361
+ #ifdef _WIN32
362
+ HDC hdc = GetDC(nullptr);
363
+ int dpi = GetDeviceCaps(hdc, LOGPIXELSX);
364
+ ReleaseDC(nullptr, hdc);
365
+ return dpi / 96.0;
366
+ #elif defined(__APPLE__)
367
+ return CGDisplayPixelsHigh(CGMainDisplayID()) / CGDisplayBounds(CGMainDisplayID()).size.height;
368
+ #else
369
+ gtk_init_check(0, nullptr);
370
+ GdkDisplay* display = gdk_display_get_default();
371
+ if (!display) return 1.0;
372
+ GdkMonitor* monitor = gdk_display_get_primary_monitor(display);
373
+ if (!monitor) return 1.0;
374
+ return gdk_monitor_get_scale_factor(monitor);
375
+ #endif
376
+ }
377
+
378
+ double Screen::getRefreshRate() {
379
+ #ifdef _WIN32
380
+ DEVMODE dm = {};
381
+ if (EnumDisplaySettings(nullptr, ENUM_CURRENT_SETTINGS, &dm)) {
382
+ return dm.dmDisplayFrequency;
383
+ }
384
+ return 60.0;
385
+ #elif defined(__APPLE__)
386
+ return CGDisplayModeGetRefreshRate(CGDisplayCopyDisplayMode(CGMainDisplayID()));
387
+ #else
388
+ gtk_init_check(0, nullptr);
389
+ GdkDisplay* display = gdk_display_get_default();
390
+ if (!display) return 60.0;
391
+ GdkMonitor* monitor = gdk_display_get_primary_monitor(display);
392
+ if (!monitor) return 60.0;
393
+ return gdk_monitor_get_refresh_rate(monitor) / 1000.0;
394
+ #endif
395
+ }
396
+
397
+ bool Screen::isMain() {
398
+ return true;
399
+ }
400
+
401
+ bool Screen::isMulti() {
402
+ #ifdef _WIN32
403
+ return GetSystemMetrics(SM_CMONITORS) > 1;
404
+ #elif defined(__APPLE__)
405
+ return CGDisplayCount() > 1;
406
+ #else
407
+ gtk_init_check(0, nullptr);
408
+ GdkDisplay* display = gdk_display_get_default();
409
+ if (!display) return false;
410
+ return gdk_display_get_n_monitors(display) > 1;
411
+ #endif
412
+ }
413
+
414
+ std::string Screen::getName() {
415
+ #ifdef _WIN32
416
+ return "Primary Screen";
417
+ #elif defined(__APPLE__)
418
+ return [[[NSScreen mainScreen] localizedName] UTF8String];
419
+ #else
420
+ return "Primary Screen";
421
+ #endif
422
+ }
423
+
424
+ }
@@ -0,0 +1,275 @@
1
+ #include "tray.hpp"
2
+ #include <iostream>
3
+ #include <algorithm>
4
+
5
+ #ifdef _WIN32
6
+ #include <windows.h>
7
+ #include <shellapi.h>
8
+ #pragma comment(lib, "shell32.lib")
9
+ #elif defined(__APPLE__)
10
+ #include <Cocoa/Cocoa.h>
11
+ #else
12
+ #include <gtk/gtk.h>
13
+ #include <gdk/gdk.h>
14
+ #endif
15
+
16
+ namespace plusui {
17
+
18
+ struct TrayIcon::Impl {
19
+ int id = 0;
20
+ std::string tooltip;
21
+ std::string iconPath;
22
+ bool visible = true;
23
+
24
+ std::function<void(int, int)> clickCallback;
25
+ std::function<void(int, int)> rightClickCallback;
26
+ std::function<void()> doubleClickCallback;
27
+ std::function<void(const std::string&)> menuItemCallback;
28
+ std::vector<TrayMenuItem> menuItems;
29
+
30
+ #ifdef _WIN32
31
+ NOTIFYICONDATAW nid = {};
32
+ HWND hwnd = nullptr;
33
+ #endif
34
+ };
35
+
36
+ TrayIcon::TrayIcon() : pImpl(std::make_unique<Impl>()) {}
37
+
38
+ TrayIcon::~TrayIcon() {
39
+ dispose();
40
+ }
41
+
42
+ TrayIcon TrayIcon::create(const std::string& tooltip, const std::string& iconPath) {
43
+ TrayIcon icon;
44
+ icon.pImpl->tooltip = tooltip;
45
+ icon.pImpl->iconPath = iconPath;
46
+
47
+ #ifdef _WIN32
48
+ icon.pImpl->nid.cbSize = sizeof(NOTIFYICONDATAW);
49
+ icon.pImpl->nid.uID = 1;
50
+ icon.pImpl->nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
51
+ icon.pImpl->nid.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
52
+
53
+ if (!tooltip.empty()) {
54
+ std::wstring wtooltip(tooltip.begin(), tooltip.end());
55
+ wcsncpy_s(icon.pImpl->nid.szTip, wtooltip.c_str(), _TRUNCATE);
56
+ }
57
+
58
+ Shell_NotifyIconW(NIM_ADD, &icon.pImpl->nid);
59
+ #endif
60
+
61
+ #ifdef __APPLE__
62
+ NSStatusItem* item = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
63
+ if (!iconPath.empty()) {
64
+ NSImage* image = [NSImage imageNamed:[NSString stringWithUTF8String:iconPath.c_str()]];
65
+ if (image) {
66
+ [item.button setImage:image];
67
+ }
68
+ }
69
+ if (!tooltip.empty()) {
70
+ [item.button setToolTip:[NSString stringWithUTF8String:tooltip.c_str()]];
71
+ }
72
+ #endif
73
+
74
+ #ifndef _WIN32
75
+ #ifndef __APPLE__
76
+ gtk_init_check(0, nullptr);
77
+ #endif
78
+ #endif
79
+
80
+ return icon;
81
+ }
82
+
83
+ void TrayIcon::setTooltip(const std::string& tooltip) {
84
+ pImpl->tooltip = tooltip;
85
+
86
+ #ifdef _WIN32
87
+ if (!tooltip.empty()) {
88
+ std::wstring wtooltip(tooltip.begin(), tooltip.end());
89
+ wcsncpy_s(pImpl->nid.szTip, wtooltip.c_str(), _TRUNCATE);
90
+ Shell_NotifyIconW(NIM_MODIFY, &pImpl->nid);
91
+ }
92
+ #endif
93
+
94
+ #ifdef __APPLE__
95
+ #endif
96
+ }
97
+
98
+ void TrayIcon::setIcon(const std::string& iconPath) {
99
+ pImpl->iconPath = iconPath;
100
+
101
+ #ifdef _WIN32
102
+ if (!iconPath.empty()) {
103
+ std::wstring wpath(iconPath.begin(), iconPath.end());
104
+ pImpl->nid.hIcon = (HICON)LoadImageW(nullptr, wpath.c_str(), IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
105
+ Shell_NotifyIconW(NIM_MODIFY, &pImpl->nid);
106
+ }
107
+ #endif
108
+
109
+ #ifdef __APPLE__
110
+ if (!iconPath.empty()) {
111
+ NSImage* image = [NSImage imageNamed:[NSString stringWithUTF8String:iconPath.c_str()]];
112
+ if (image) {
113
+ [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength].button.image = image;
114
+ }
115
+ }
116
+ #endif
117
+ }
118
+
119
+ void TrayIcon::setIconFromData(const std::vector<uint8_t>& data, int width, int height) {
120
+ #ifdef _WIN32
121
+ if (!data.empty()) {
122
+ BITMAPINFO bmi = {};
123
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
124
+ bmi.bmiHeader.biWidth = width;
125
+ bmi.bmiHeader.biHeight = -height;
126
+ bmi.bmiHeader.biPlanes = 1;
127
+ bmi.bmiHeader.biBitCount = 32;
128
+ bmi.bmiHeader.biCompression = BI_RGB;
129
+
130
+ void* bits = nullptr;
131
+ HBITMAP hbm = CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &bits, nullptr, 0);
132
+ if (hbm && bits && data.size() >= (size_t)width * height * 4) {
133
+ memcpy(bits, data.data(), width * height * 4);
134
+
135
+ ICONINFO ii = {};
136
+ ii.fIcon = TRUE;
137
+ ii.hbmColor = hbm;
138
+ ii.hbmMask = hbm;
139
+
140
+ HICON hIcon = CreateIconIndirect(&ii);
141
+ if (hIcon) {
142
+ pImpl->nid.hIcon = hIcon;
143
+ Shell_NotifyIconW(NIM_MODIFY, &pImpl->nid);
144
+ DestroyIcon(hIcon);
145
+ }
146
+ DeleteObject(hbm);
147
+ }
148
+ }
149
+ #endif
150
+ }
151
+
152
+ void TrayIcon::show() {
153
+ pImpl->visible = true;
154
+ #ifdef _WIN32
155
+ pImpl->nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
156
+ Shell_NotifyIconW(NIM_ADD, &pImpl->nid);
157
+ #endif
158
+ }
159
+
160
+ void TrayIcon::hide() {
161
+ pImpl->visible = false;
162
+ #ifdef _WIN32
163
+ Shell_NotifyIconW(NIM_DELETE, &pImpl->nid);
164
+ #endif
165
+ }
166
+
167
+ bool TrayIcon::isVisible() const {
168
+ return pImpl->visible;
169
+ }
170
+
171
+ void TrayIcon::setMenu(const std::vector<TrayMenuItem>& items) {
172
+ pImpl->menuItems = items;
173
+
174
+ #ifdef _WIN32
175
+ HMENU hMenu = CreatePopupMenu();
176
+
177
+ for (const auto& item : items) {
178
+ if (item.separator) {
179
+ AppendMenuW(hMenu, MF_SEPARATOR, 0, nullptr);
180
+ } else {
181
+ std::wstring label(item.label.begin(), item.label.end());
182
+ UINT flags = MF_STRING;
183
+ if (!item.enabled) flags |= MF_DISABLED;
184
+ if (item.checked) flags |= MF_CHECKED;
185
+
186
+ AppendMenuW(hMenu, flags, (UINT_PTR)item.id.c_str(), label.c_str());
187
+
188
+ if (!item.submenu.empty()) {
189
+ HMENU hSubMenu = CreatePopupMenu();
190
+ for (const auto& sub : item.submenu) {
191
+ std::wstring subLabel(sub.label.begin(), sub.label.end());
192
+ AppendMenuW(hSubMenu, MF_STRING, (UINT_PTR)sub.id.c_str(), subLabel.c_str());
193
+ }
194
+ SetMenuItemInfoW(hMenu, GetMenuItemCount(hMenu) - 1, TRUE, nullptr);
195
+ }
196
+ }
197
+ }
198
+
199
+ pImpl->nid.hWnd = GetForegroundWindow();
200
+ pImpl->nid.uCallbackMessage = WM_USER;
201
+
202
+ Shell_NotifyIconW(NIM_MODIFY, &pImpl->nid);
203
+ #endif
204
+
205
+ #ifdef __APPLE__
206
+ NSMenu* menu = [[NSMenu alloc] init];
207
+
208
+ for (const auto& item : items) {
209
+ if (item.separator) {
210
+ [menu addItem:[NSMenuItem separatorItem]];
211
+ } else {
212
+ NSMenuItem* menuItem = [[NSMenuItem alloc]
213
+ initWithTitle:[NSString stringWithUTF8String:item.label.c_str()]
214
+ action:@selector(trayMenuItemClicked:)
215
+ keyEquivalent:@""];
216
+ menuItem.enabled = item.enabled;
217
+ [menu addItem:menuItem];
218
+ }
219
+ }
220
+ #endif
221
+ }
222
+
223
+ void TrayIcon::setContextMenu(const std::vector<TrayMenuItem>& items) {
224
+ setMenu(items);
225
+ }
226
+
227
+ void TrayIcon::onClick(std::function<void(int, int)> callback) {
228
+ pImpl->clickCallback = callback;
229
+ }
230
+
231
+ void TrayIcon::onRightClick(std::function<void(int, int)> callback) {
232
+ pImpl->rightClickCallback = callback;
233
+ }
234
+
235
+ void TrayIcon::onDoubleClick(std::function<void()> callback) {
236
+ pImpl->doubleClickCallback = callback;
237
+ }
238
+
239
+ void TrayIcon::onMenuItemClick(std::function<void(const std::string&)> callback) {
240
+ pImpl->menuItemCallback = callback;
241
+ }
242
+
243
+ void TrayIcon::dispose() {
244
+ #ifdef _WIN32
245
+ Shell_NotifyIconW(NIM_DELETE, &pImpl->nid);
246
+ #endif
247
+ }
248
+
249
+ struct TrayManager::Impl {
250
+ std::vector<TrayIcon*> icons;
251
+ std::function<void(TrayIcon&)> onCreatedCallback;
252
+ std::function<void(int)> onDestroyedCallback;
253
+ };
254
+
255
+ TrayManager& TrayManager::instance() {
256
+ static TrayManager inst;
257
+ return inst;
258
+ }
259
+
260
+ std::vector<TrayIcon*> TrayManager::getIcons() {
261
+ return pImpl->icons;
262
+ }
263
+
264
+ void TrayManager::onTrayCreated(std::function<void(TrayIcon&)> callback) {
265
+ pImpl->onCreatedCallback = callback;
266
+ }
267
+
268
+ void TrayManager::onTrayDestroyed(std::function<void(int)> callback) {
269
+ pImpl->onDestroyedCallback = callback;
270
+ }
271
+
272
+ void TrayManager::refresh() {
273
+ }
274
+
275
+ }