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.
- package/Core/CMakeLists.txt +34 -0
- package/Core/README.md +29 -0
- package/Core/build/win32/x64/ALL_BUILD.vcxproj +185 -0
- package/Core/build/win32/x64/ALL_BUILD.vcxproj.filters +8 -0
- package/Core/build/win32/x64/CMakeCache.txt +335 -0
- package/Core/build/win32/x64/CMakeFiles/11f7f2f432927ec8d1861dc42d4bd679/INSTALL_force.rule +1 -0
- package/Core/build/win32/x64/CMakeFiles/11f7f2f432927ec8d1861dc42d4bd679/generate.stamp.rule +1 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CMakeCXXCompiler.cmake +104 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CMakeDetermineCompilerABI_CXX.bin +0 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CMakeRCCompiler.cmake +6 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CMakeSystem.cmake +15 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/CMakeCXXCompilerId.cpp +949 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/CompilerIdCXX.exe +0 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/CompilerIdCXX.vcxproj +72 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CMakeCXXCompilerId.obj +0 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.exe.recipe +11 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.command.1.tlog +2 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.read.1.tlog +4 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.write.1.tlog +2 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/Cl.items.tlog +1 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CompilerIdCXX.lastbuildstate +2 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.command.1.tlog +2 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.read.1.tlog +22 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.secondary.1.tlog +1 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.write.1.tlog +2 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/VCTargetsPath/x64/Debug/VCTargetsPath.recipe +11 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/VCTargetsPath/x64/Debug/VCTargetsPath.tlog/VCTargetsPath.lastbuildstate +2 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/VCTargetsPath.txt +1 -0
- package/Core/build/win32/x64/CMakeFiles/4.2.3/VCTargetsPath.vcxproj +31 -0
- package/Core/build/win32/x64/CMakeFiles/CMakeConfigureLog.yaml +2698 -0
- package/Core/build/win32/x64/CMakeFiles/InstallScripts.json +7 -0
- package/Core/build/win32/x64/CMakeFiles/TargetDirectories.txt +4 -0
- package/Core/build/win32/x64/CMakeFiles/cmake.check_cache +1 -0
- package/Core/build/win32/x64/CMakeFiles/generate.stamp +1 -0
- package/Core/build/win32/x64/CMakeFiles/generate.stamp.depend +34 -0
- package/Core/build/win32/x64/CMakeFiles/generate.stamp.list +1 -0
- package/Core/build/win32/x64/Capsule.sln +67 -0
- package/Core/build/win32/x64/INSTALL.vcxproj +209 -0
- package/Core/build/win32/x64/INSTALL.vcxproj.filters +13 -0
- package/Core/build/win32/x64/ZERO_CHECK.vcxproj +179 -0
- package/Core/build/win32/x64/ZERO_CHECK.vcxproj.filters +13 -0
- package/Core/build/win32/x64/capsule.dir/Release/capsule.tlog/CL.command.1.tlog +2 -0
- package/Core/build/win32/x64/capsule.dir/Release/capsule.tlog/CustomBuild.command.1.tlog +10 -0
- package/Core/build/win32/x64/capsule.dir/Release/capsule.tlog/CustomBuild.read.1.tlog +33 -0
- package/Core/build/win32/x64/capsule.dir/Release/capsule.tlog/CustomBuild.write.1.tlog +2 -0
- package/Core/build/win32/x64/capsule.dir/Release/capsule.tlog/capsule.lastbuildstate +2 -0
- package/Core/build/win32/x64/capsule.dir/Release/capsule.tlog/unsuccessfulbuild +0 -0
- package/Core/build/win32/x64/capsule.vcxproj +332 -0
- package/Core/build/win32/x64/capsule.vcxproj.filters +25 -0
- package/Core/build/win32/x64/cmake_install.cmake +72 -0
- package/Core/build/win32/x64/x64/Release/ZERO_CHECK/ZERO_CHECK.recipe +11 -0
- package/Core/build/win32/x64/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.command.1.tlog +10 -0
- package/Core/build/win32/x64/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.read.1.tlog +34 -0
- package/Core/build/win32/x64/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.write.1.tlog +2 -0
- package/Core/build/win32/x64/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate +2 -0
- package/Core/include/app.hpp +121 -0
- package/Core/include/display.hpp +90 -0
- package/Core/include/keyboard.hpp +135 -0
- package/Core/include/menu.hpp +79 -0
- package/Core/include/tray.hpp +81 -0
- package/Core/include/window.hpp +106 -0
- package/Core/src/app.cpp +311 -0
- package/Core/src/display.cpp +424 -0
- package/Core/src/tray.cpp +275 -0
- package/Core/src/window.cpp +528 -0
- package/Core/vendor/webview.h +551 -0
- package/dist/index.d.ts +205 -0
- package/dist/index.js +198 -0
- package/package.json +19 -0
- 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
|
+
}
|