windowpp 0.1.1

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 (88) hide show
  1. package/bin/windowpp.js +86 -0
  2. package/cmake/embed_assets.py +144 -0
  3. package/framework/CMakeLists.txt +176 -0
  4. package/framework/include/windowpp/windowpp.h +704 -0
  5. package/framework/src/AppData/API/AppData.ts +137 -0
  6. package/framework/src/AppData/appdata_bridge.h +138 -0
  7. package/framework/src/AppData/appdata_manager.cpp +126 -0
  8. package/framework/src/AppData/appdata_manager.h +3 -0
  9. package/framework/src/FileSystem/API/FileSystem.ts +389 -0
  10. package/framework/src/FileSystem/Linux/filesearch.cpp +148 -0
  11. package/framework/src/FileSystem/Linux/readfile.cpp +79 -0
  12. package/framework/src/FileSystem/Linux/savefile.cpp +333 -0
  13. package/framework/src/FileSystem/MacOS/filesearch.cpp +149 -0
  14. package/framework/src/FileSystem/MacOS/readfile.cpp +80 -0
  15. package/framework/src/FileSystem/MacOS/savefile.cpp +264 -0
  16. package/framework/src/FileSystem/Windows/filesearch.cpp +195 -0
  17. package/framework/src/FileSystem/Windows/readfile.cpp +122 -0
  18. package/framework/src/FileSystem/Windows/savefile.cpp +290 -0
  19. package/framework/src/FileSystem/file_index_service.cpp +262 -0
  20. package/framework/src/FileSystem/file_index_service.h +55 -0
  21. package/framework/src/FileSystem/filesystem_bridge.h +243 -0
  22. package/framework/src/FileSystem/filesystem_handler.h +93 -0
  23. package/framework/src/FileSystem/filesystem_json.h +241 -0
  24. package/framework/src/FileSystem/filesystem_search_service.cpp +414 -0
  25. package/framework/src/FileSystem/filesystem_search_service.h +94 -0
  26. package/framework/src/Input/API/Input.ts +161 -0
  27. package/framework/src/Input/Linux/linux_key_utils.h +135 -0
  28. package/framework/src/Input/MacOS/macos_key_utils.h +137 -0
  29. package/framework/src/Input/Windows/win32_key_utils.h +199 -0
  30. package/framework/src/Input/input_bridge.h +192 -0
  31. package/framework/src/Input/input_service.cpp +584 -0
  32. package/framework/src/Input/input_service.h +21 -0
  33. package/framework/src/application.cpp +29 -0
  34. package/framework/src/common/hit_test.cpp +40 -0
  35. package/framework/src/common/image_loader.cpp +24 -0
  36. package/framework/src/common/paths.cpp +75 -0
  37. package/framework/src/filedrop/filedrop.cpp +316 -0
  38. package/framework/src/filedrop/filedrop.css +421 -0
  39. package/framework/src/filedrop/filedrop.hpp +92 -0
  40. package/framework/src/filedrop/filedrop.ts +183 -0
  41. package/framework/src/platform/API/App.ts +156 -0
  42. package/framework/src/platform/API/Window.ts +249 -0
  43. package/framework/src/platform/linux/app_linux.cpp +256 -0
  44. package/framework/src/platform/linux/app_linux.h +64 -0
  45. package/framework/src/platform/linux/linux_helpers.cpp +26 -0
  46. package/framework/src/platform/linux/linux_helpers.h +19 -0
  47. package/framework/src/platform/linux/tray_linux.cpp +21 -0
  48. package/framework/src/platform/linux/tray_linux.h +26 -0
  49. package/framework/src/platform/linux/window_linux.cpp +256 -0
  50. package/framework/src/platform/linux/window_linux.h +70 -0
  51. package/framework/src/platform/macos/app_macos.h +59 -0
  52. package/framework/src/platform/macos/app_macos.mm +223 -0
  53. package/framework/src/platform/macos/macos_helpers.h +21 -0
  54. package/framework/src/platform/macos/tray_macos.h +22 -0
  55. package/framework/src/platform/macos/tray_macos.mm +53 -0
  56. package/framework/src/platform/macos/window_macos.h +74 -0
  57. package/framework/src/platform/macos/window_macos.mm +318 -0
  58. package/framework/src/platform/platform_bridge.h +514 -0
  59. package/framework/src/platform/platform_factory.cpp +33 -0
  60. package/framework/src/platform/platform_factory.h +19 -0
  61. package/framework/src/platform/win32/app_win32.cpp +572 -0
  62. package/framework/src/platform/win32/app_win32.h +83 -0
  63. package/framework/src/platform/win32/tray_win32.cpp +57 -0
  64. package/framework/src/platform/win32/tray_win32.h +30 -0
  65. package/framework/src/platform/win32/win32_helpers.h +61 -0
  66. package/framework/src/platform/win32/window_win32.cpp +267 -0
  67. package/framework/src/platform/win32/window_win32.h +79 -0
  68. package/framework/src/renderer/webgpu.h +128 -0
  69. package/framework/src/renderer/webview/include/WebView2.h +48014 -0
  70. package/framework/src/renderer/webview/include/WebView2EnvironmentOptions.h +342 -0
  71. package/framework/src/renderer/webview/webview.h +13 -0
  72. package/framework/src/renderer/webview/webview_linux.cpp +392 -0
  73. package/framework/src/renderer/webview/webview_macos.mm +388 -0
  74. package/framework/src/renderer/webview/webview_win32.cpp +688 -0
  75. package/framework/src/renderer/webview/x64/WebView2Loader.dll +0 -0
  76. package/framework/src/renderer/webview/x64/WebView2Loader.lib +0 -0
  77. package/framework/src/renderer/webview/x64/WebView2LoaderStatic.lib +0 -0
  78. package/lib/build.js +112 -0
  79. package/lib/create.js +283 -0
  80. package/lib/dev.js +155 -0
  81. package/package.json +24 -0
  82. package/scripts/publish.js +67 -0
  83. package/scripts/sync-framework.js +73 -0
  84. package/templates/solid/CMakeLists.txt +56 -0
  85. package/templates/solid/frontend/package.json +22 -0
  86. package/templates/solid/frontend/vite.config.ts +25 -0
  87. package/templates/solid/main.cpp +72 -0
  88. package/templates/solid/package.json +12 -0
@@ -0,0 +1,572 @@
1
+ // ============================================================================
2
+ // app_win32.cpp — Win32 Application implementation
3
+ // ============================================================================
4
+
5
+ #define WIN32_LEAN_AND_MEAN
6
+ #define NOMINMAX
7
+ #include "app_win32.h"
8
+ #include "window_win32.h"
9
+ #include "win32_helpers.h"
10
+ #include "../../Input/input_service.h"
11
+ #include "../../Input/Windows/win32_key_utils.h"
12
+ #include "../../renderer/webview/webview.h"
13
+ #include <algorithm>
14
+
15
+ namespace wpp {
16
+
17
+ namespace {
18
+
19
+ constexpr wchar_t kWindowClassName[] = L"WindowPPClass";
20
+
21
+ bool use_native_window_file_drop(const WindowConfig& config) {
22
+ return config.file_drop && !config.webview.enabled;
23
+ }
24
+
25
+ }
26
+
27
+ Win32Application::Win32Application(const AppConfig& config) : config_(config) {
28
+ win32::WindowClass wc("WindowPPApp", wnd_proc);
29
+ class_atom_ = wc.atom;
30
+
31
+ hwnd_ = CreateWindowExW(
32
+ 0,
33
+ MAKEINTATOM(class_atom_),
34
+ win32::to_wide(config.name).c_str(),
35
+ WS_OVERLAPPEDWINDOW,
36
+ CW_USEDEFAULT, CW_USEDEFAULT,
37
+ 800, 600,
38
+ nullptr, nullptr,
39
+ GetModuleHandleW(nullptr),
40
+ this
41
+ );
42
+
43
+ if (!hwnd_) {
44
+ return;
45
+ }
46
+
47
+ SetWindowLongPtr(hwnd_, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
48
+
49
+ input::detail::set_dispatch([this](std::function<void()> fn) {
50
+ dispatch(std::move(fn));
51
+ });
52
+ input::detail::set_global_backend(
53
+ [this](const std::string& id, const input::Shortcut& shortcut, std::string* error) {
54
+ return register_global_hotkey(id, shortcut, error);
55
+ },
56
+ [this](const std::string& id) {
57
+ unregister_global_hotkey(id);
58
+ });
59
+
60
+ if (config_.single_instance) {
61
+ CreateMutexW(nullptr, TRUE, win32::to_wide(config.identifier).c_str());
62
+ }
63
+
64
+ if (config_.run_on_startup) {
65
+ HKEY hKey;
66
+ RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
67
+ 0, KEY_SET_VALUE, &hKey);
68
+ RegSetValueExW(hKey, win32::to_wide(config.name).c_str(), 0, REG_SZ,
69
+ (BYTE*)win32::to_wide("").c_str(), 0);
70
+ RegCloseKey(hKey);
71
+ }
72
+
73
+ if (config_.on_ready) {
74
+ config_.on_ready();
75
+ }
76
+
77
+ if (config_.tray_enabled) {
78
+ set_tray(config_.tray);
79
+ }
80
+ }
81
+
82
+ Win32Application::~Win32Application() {
83
+ shutting_down_ = true;
84
+ clear_global_hotkeys();
85
+ input::detail::clear_global_backend();
86
+ if (hwnd_ && IsWindow(hwnd_)) {
87
+ DestroyWindow(hwnd_);
88
+ }
89
+ }
90
+
91
+ Window* Win32Application::create_window(const WindowConfig& config) {
92
+ auto id = config.id.empty() ? "main" : config.id;
93
+
94
+ DWORD style = WS_OVERLAPPEDWINDOW;
95
+ DWORD ex_style = 0;
96
+
97
+ if (config.frameless) {
98
+ style = WS_POPUP | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
99
+ }
100
+ if (config.transparent) {
101
+ ex_style |= WS_EX_LAYERED | WS_EX_COMPOSITED;
102
+ }
103
+ if (config.always_on_top) {
104
+ ex_style |= WS_EX_TOPMOST;
105
+ }
106
+ if (config.modal && !config.parent_id.empty()) {
107
+ ex_style |= WS_EX_DLGMODALFRAME;
108
+ }
109
+
110
+ HWND parent = nullptr;
111
+ if (!config.parent_id.empty()) {
112
+ auto parent_win = get_window(config.parent_id);
113
+ if (parent_win) {
114
+ parent = static_cast<Win32Window*>(parent_win)->hwnd();
115
+ }
116
+ }
117
+
118
+ int x = config.x >= 0 ? config.x : CW_USEDEFAULT;
119
+ int y = config.y >= 0 ? config.y : CW_USEDEFAULT;
120
+ int w = config.width;
121
+ int h = config.height;
122
+
123
+ HWND hwnd = CreateWindowExW(
124
+ ex_style,
125
+ MAKEINTATOM(class_atom_),
126
+ win32::to_wide(config.title).c_str(),
127
+ style,
128
+ x, y, w, h,
129
+ parent, nullptr,
130
+ GetModuleHandleW(nullptr),
131
+ this
132
+ );
133
+
134
+ if (!hwnd) {
135
+ return nullptr;
136
+ }
137
+
138
+ if (config.frameless) {
139
+ SetWindowLongPtr(hwnd, GWL_STYLE,
140
+ GetWindowLongPtr(hwnd, GWL_STYLE) & ~(WS_CAPTION | WS_THICKFRAME));
141
+ MARGINS margins = { -1, -1, -1, -1 };
142
+ DwmExtendFrameIntoClientArea(hwnd, &margins);
143
+ }
144
+
145
+ auto window = std::make_unique<Win32Window>(config, hwnd);
146
+
147
+ if (use_native_window_file_drop(config)) {
148
+ DragAcceptFiles(hwnd, TRUE);
149
+ }
150
+
151
+ if (config.initial_state == WindowState::Hidden) {
152
+ ShowWindow(hwnd, SW_HIDE);
153
+ } else {
154
+ ShowWindow(hwnd, SW_SHOW);
155
+ }
156
+
157
+ if (config.webview.enabled) {
158
+ auto webview_config = config.webview;
159
+ webview_config.frameless = config.frameless;
160
+ webview_config.scrollbars = config.scrollbars;
161
+ webview_config.file_drop = config.file_drop;
162
+ window->set_webview(create_webview(webview_config, hwnd));
163
+ if (auto* webview = window->webview()) {
164
+ webview->set_drag_drop_enabled(config.file_drop);
165
+ }
166
+ }
167
+
168
+ auto* ptr = window.get();
169
+ windows_[id] = std::move(window);
170
+ hwnd_to_id_[hwnd] = id;
171
+
172
+ if (config.center) {
173
+ ptr->center();
174
+ }
175
+
176
+ if (config.on_created) {
177
+ config.on_created();
178
+ }
179
+
180
+ return ptr;
181
+ }
182
+
183
+ Window* Win32Application::get_window(const std::string& id) {
184
+ auto it = windows_.find(id);
185
+ return it != windows_.end() ? it->second.get() : nullptr;
186
+ }
187
+
188
+ std::vector<Window*> Win32Application::all_windows() {
189
+ std::vector<Window*> result;
190
+ for (auto& [id, win] : windows_) {
191
+ result.push_back(win.get());
192
+ }
193
+ return result;
194
+ }
195
+
196
+ Win32Window* Win32Application::get_window_by_hwnd(HWND hwnd) {
197
+ auto it = hwnd_to_id_.find(hwnd);
198
+ if (it == hwnd_to_id_.end()) return nullptr;
199
+ auto wit = windows_.find(it->second);
200
+ return wit != windows_.end() ? wit->second.get() : nullptr;
201
+ }
202
+
203
+ void Win32Application::run() {
204
+ running_ = true;
205
+ MSG msg;
206
+ while (running_) {
207
+ BOOL ret = GetMessageW(&msg, nullptr, 0, 0);
208
+ if (ret == 0 || ret == -1) {
209
+ break;
210
+ }
211
+ TranslateMessage(&msg);
212
+ DispatchMessageW(&msg);
213
+ }
214
+ }
215
+
216
+ void Win32Application::quit() {
217
+ running_ = false;
218
+ should_quit_ = true;
219
+ if (config_.on_quit) {
220
+ config_.on_quit();
221
+ }
222
+ PostQuitMessage(0);
223
+ }
224
+
225
+ void Win32Application::set_tray(const TrayConfig& config) {
226
+ tray_ = std::make_unique<Win32Tray>(config, hwnd_);
227
+ }
228
+
229
+ void Win32Application::remove_tray() {
230
+ tray_.reset();
231
+ }
232
+
233
+ std::vector<MonitorInfo> Win32Application::monitors() {
234
+ std::vector<MonitorInfo> result;
235
+ EnumDisplayMonitors(nullptr, nullptr, [](HMONITOR hmon, HDC, LPRECT, LPARAM lp) -> BOOL {
236
+ auto* result = reinterpret_cast<std::vector<MonitorInfo>*>(lp);
237
+ MonitorInfo mi;
238
+ MONITORINFOEXW mi_ex = { sizeof(mi_ex) };
239
+ GetMonitorInfoW(hmon, &mi_ex);
240
+ mi.name = win32::from_wide(mi_ex.szDevice);
241
+ mi.bounds = { (int)mi_ex.rcMonitor.left, (int)mi_ex.rcMonitor.top,
242
+ (int)(mi_ex.rcMonitor.right - mi_ex.rcMonitor.left),
243
+ (int)(mi_ex.rcMonitor.bottom - mi_ex.rcMonitor.top) };
244
+ mi.work_area = { (int)mi_ex.rcWork.left, (int)mi_ex.rcWork.top,
245
+ (int)(mi_ex.rcWork.right - mi_ex.rcWork.left),
246
+ (int)(mi_ex.rcWork.bottom - mi_ex.rcWork.top) };
247
+ mi.scale_factor = 1.0f;
248
+ mi.is_primary = (mi_ex.dwFlags & MONITORINFOF_PRIMARY) != 0;
249
+ result->push_back(mi);
250
+ return TRUE;
251
+ }, reinterpret_cast<LPARAM>(&result));
252
+ return result;
253
+ }
254
+
255
+ MonitorInfo Win32Application::primary_monitor() {
256
+ auto all = monitors();
257
+ for (auto& m : all) {
258
+ if (m.is_primary) return m;
259
+ }
260
+ return all.front();
261
+ }
262
+
263
+ std::string Win32Application::clipboard_text() {
264
+ if (!OpenClipboard(nullptr)) return {};
265
+ HANDLE data = GetClipboardData(CF_UNICODETEXT);
266
+ if (!data) {
267
+ CloseClipboard();
268
+ return {};
269
+ }
270
+ std::wstring wstr = (wchar_t*)data;
271
+ CloseClipboard();
272
+ return win32::from_wide(wstr);
273
+ }
274
+
275
+ void Win32Application::set_clipboard_text(const std::string& text) {
276
+ if (!OpenClipboard(nullptr)) return;
277
+ EmptyClipboard();
278
+ std::wstring wstr = win32::to_wide(text);
279
+ HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (wstr.size() + 1) * sizeof(wchar_t));
280
+ wchar_t* dst = (wchar_t*)GlobalLock(mem);
281
+ wcscpy_s(dst, wstr.size() + 1, wstr.c_str());
282
+ GlobalUnlock(mem);
283
+ SetClipboardData(CF_UNICODETEXT, mem);
284
+ CloseClipboard();
285
+ }
286
+
287
+ void Win32Application::send_message(const std::string& window_id,
288
+ const std::string& event,
289
+ const std::string& payload) {
290
+ auto it = message_handlers_.find(window_id);
291
+ if (it != message_handlers_.end()) {
292
+ for (auto& handler : it->second) {
293
+ handler(event, payload);
294
+ }
295
+ }
296
+ }
297
+
298
+ void Win32Application::on_message(const std::string& window_id, MessageHandler handler) {
299
+ message_handlers_[window_id].push_back(handler);
300
+ }
301
+
302
+ Application::TimerId Win32Application::set_timeout(uint32_t ms, std::function<void()> callback) {
303
+ return SetTimer(hwnd_, next_timer_id_++, ms, nullptr);
304
+ }
305
+
306
+ Application::TimerId Win32Application::set_interval(uint32_t ms, std::function<void()> callback) {
307
+ return SetTimer(hwnd_, next_timer_id_++, ms, nullptr);
308
+ }
309
+
310
+ void Win32Application::clear_timer(TimerId id) {
311
+ KillTimer(hwnd_, id);
312
+ }
313
+
314
+ void Win32Application::dispatch(std::function<void()> fn) {
315
+ {
316
+ std::lock_guard<std::mutex> lock(dispatch_mutex_);
317
+ dispatch_queue_.push(std::move(fn));
318
+ }
319
+ PostMessageW(hwnd_, WM_APP, 0, 0);
320
+ }
321
+
322
+ LRESULT CALLBACK Win32Application::wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
323
+ if (msg == WM_CREATE) {
324
+ auto* cs = reinterpret_cast<CREATESTRUCT*>(lparam);
325
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(cs->lpCreateParams));
326
+ }
327
+ auto* app = reinterpret_cast<Win32Application*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
328
+ if (app) {
329
+ return app->handle_message(hwnd, msg, wparam, lparam);
330
+ }
331
+ return DefWindowProcW(hwnd, msg, wparam, lparam);
332
+ }
333
+
334
+ LRESULT Win32Application::handle_message(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
335
+ switch (msg) {
336
+ case WM_APP: {
337
+ std::queue<std::function<void()>> pending;
338
+ {
339
+ std::lock_guard<std::mutex> lock(dispatch_mutex_);
340
+ std::swap(pending, dispatch_queue_);
341
+ }
342
+ while (!pending.empty()) {
343
+ auto fn = std::move(pending.front());
344
+ pending.pop();
345
+ if (fn) {
346
+ fn();
347
+ }
348
+ }
349
+ return 0;
350
+ }
351
+
352
+ case WM_CLOSE:
353
+ if (hwnd == hwnd_) {
354
+ if (config_.hide_on_close) {
355
+ ShowWindow(hwnd, SW_HIDE);
356
+ return 0;
357
+ }
358
+ quit();
359
+ } else {
360
+ auto* win = get_window_by_hwnd(hwnd);
361
+ if (win && win->config().on_close_requested && !win->config().on_close_requested()) {
362
+ return 0;
363
+ }
364
+ DestroyWindow(hwnd);
365
+ }
366
+ return 0;
367
+
368
+ case WM_HOTKEY: {
369
+ if (hwnd != hwnd_) {
370
+ return 0;
371
+ }
372
+ auto it = hotkey_ids_.find(static_cast<int>(wparam));
373
+ if (it != hotkey_ids_.end()) {
374
+ input::detail::trigger_global_shortcut(it->second);
375
+ }
376
+ return 0;
377
+ }
378
+
379
+ case WM_DESTROY:
380
+ if (shutting_down_) return 0;
381
+ if (hwnd == hwnd_) {
382
+ PostQuitMessage(0);
383
+ } else {
384
+ auto id_it = hwnd_to_id_.find(hwnd);
385
+ if (id_it != hwnd_to_id_.end()) {
386
+ std::string id = id_it->second;
387
+ hwnd_to_id_.erase(id_it);
388
+ auto wit = windows_.find(id);
389
+ if (wit != windows_.end()) {
390
+ auto* win = wit->second.get();
391
+ win->nullify_hwnd();
392
+ if (win->config().on_closed) win->config().on_closed();
393
+ windows_.erase(wit);
394
+ }
395
+ if (config_.quit_on_last_close && windows_.empty()) {
396
+ quit();
397
+ }
398
+ }
399
+ }
400
+ return 0;
401
+
402
+ case WM_SIZE: {
403
+ int w = LOWORD(lparam);
404
+ int h = HIWORD(lparam);
405
+ ResizeEvent e{{0, 0}, {w, h}};
406
+ auto* win = get_window_by_hwnd(hwnd);
407
+ if (win) {
408
+ win->resize_webview(w, h);
409
+ if (win->config().on_resize) {
410
+ win->config().on_resize(e);
411
+ }
412
+ }
413
+ return 0;
414
+ }
415
+
416
+ case WM_MOVE: {
417
+ int x = LOWORD(lparam);
418
+ int y = HIWORD(lparam);
419
+ MoveEvent e{{x, y}, {x, y}};
420
+ auto* win = get_window_by_hwnd(hwnd);
421
+ if (win && win->config().on_move) {
422
+ win->config().on_move(e);
423
+ }
424
+ return 0;
425
+ }
426
+
427
+ case WM_KEYDOWN:
428
+ case WM_SYSKEYDOWN:
429
+ case WM_KEYUP:
430
+ case WM_SYSKEYUP: {
431
+ auto* win = get_window_by_hwnd(hwnd);
432
+ if (!win) {
433
+ return DefWindowProcW(hwnd, msg, wparam, lparam);
434
+ }
435
+
436
+ if (win->webview()) {
437
+ return DefWindowProcW(hwnd, msg, wparam, lparam);
438
+ }
439
+
440
+ auto event = input::win32::make_key_event(msg, wparam, lparam, win->id());
441
+ input::detail::emit_key_event(event);
442
+
443
+ if (event.type == input::KeyEventType::KeyDown) {
444
+ if (win->config().on_key_down) {
445
+ win->config().on_key_down(event);
446
+ }
447
+ } else if (win->config().on_key_up) {
448
+ win->config().on_key_up(event);
449
+ }
450
+ return 0;
451
+ }
452
+
453
+ case WM_NCHITTEST: {
454
+ auto* win = get_window_by_hwnd(hwnd);
455
+ if (!win || !win->config().frameless) return DefWindowProcW(hwnd, msg, wparam, lparam);
456
+
457
+ POINT pt = { (int)(short)LOWORD(lparam), (int)(short)HIWORD(lparam) };
458
+ ScreenToClient(hwnd, &pt);
459
+
460
+ switch (win->hit_test({pt.x, pt.y})) {
461
+ case HitTestRegion::Caption: return HTCAPTION;
462
+ case HitTestRegion::ResizeTop: return HTTOP;
463
+ case HitTestRegion::ResizeBottom: return HTBOTTOM;
464
+ case HitTestRegion::ResizeLeft: return HTLEFT;
465
+ case HitTestRegion::ResizeRight: return HTRIGHT;
466
+ case HitTestRegion::ResizeTopLeft: return HTTOPLEFT;
467
+ case HitTestRegion::ResizeTopRight: return HTTOPRIGHT;
468
+ case HitTestRegion::ResizeBottomLeft: return HTBOTTOMLEFT;
469
+ case HitTestRegion::ResizeBottomRight:return HTBOTTOMRIGHT;
470
+ default: return HTCLIENT;
471
+ }
472
+ }
473
+
474
+ case WM_ERASEBKGND: {
475
+ auto* win = get_window_by_hwnd(hwnd);
476
+ Color bg = win ? win->config().background : Color{255, 255, 255, 255};
477
+ HDC hdc = reinterpret_cast<HDC>(wparam);
478
+ RECT rc;
479
+ GetClientRect(hwnd, &rc);
480
+ HBRUSH brush = CreateSolidBrush(RGB(bg.r, bg.g, bg.b));
481
+ FillRect(hdc, &rc, brush);
482
+ DeleteObject(brush);
483
+ return 1;
484
+ }
485
+
486
+ case WM_TIMER:
487
+ return 0;
488
+
489
+ case WM_DROPFILES: {
490
+ auto* win = get_window_by_hwnd(hwnd);
491
+ if (win && use_native_window_file_drop(win->config()) && win->config().on_file_drop) {
492
+ HDROP hDrop = reinterpret_cast<HDROP>(wparam);
493
+ UINT fileCount = DragQueryFileW(hDrop, 0xFFFFFFFF, nullptr, 0);
494
+ std::vector<std::string> paths;
495
+ for (UINT i = 0; i < fileCount; ++i) {
496
+ UINT pathLen = DragQueryFileW(hDrop, i, nullptr, 0);
497
+ if (pathLen > 0) {
498
+ std::wstring wpath(pathLen + 1, L'\0');
499
+ DragQueryFileW(hDrop, i, &wpath[0], pathLen + 1);
500
+ wpath.resize(pathLen);
501
+ paths.push_back(win32::from_wide(wpath));
502
+ }
503
+ }
504
+ DragFinish(hDrop);
505
+ if (!paths.empty()) {
506
+ DropEvent e;
507
+ e.paths = std::move(paths);
508
+ e.position = {0, 0};
509
+ win->config().on_file_drop(e);
510
+ }
511
+ }
512
+ return 0;
513
+ }
514
+
515
+ default:
516
+ return DefWindowProcW(hwnd, msg, wparam, lparam);
517
+ }
518
+ }
519
+
520
+ bool Win32Application::register_global_hotkey(const std::string& id,
521
+ const input::Shortcut& shortcut,
522
+ std::string* error) {
523
+ unregister_global_hotkey(id);
524
+
525
+ if (input::win32::is_modifier_key(shortcut.key)) {
526
+ if (error) {
527
+ *error = "Modifier-only global hotkeys are not supported on Windows";
528
+ }
529
+ return false;
530
+ }
531
+
532
+ const UINT vk = input::win32::virtual_key_from_key(shortcut.key);
533
+ if (vk == 0) {
534
+ if (error) {
535
+ *error = "Unsupported key for Windows global hotkey";
536
+ }
537
+ return false;
538
+ }
539
+
540
+ const int hotkey_id = next_hotkey_id_++;
541
+ if (!RegisterHotKey(hwnd_, hotkey_id, input::win32::hotkey_modifiers_from_shortcut(shortcut), vk)) {
542
+ if (error) {
543
+ *error = "RegisterHotKey failed";
544
+ }
545
+ return false;
546
+ }
547
+
548
+ hotkey_ids_[hotkey_id] = id;
549
+ hotkey_name_to_id_[id] = hotkey_id;
550
+ return true;
551
+ }
552
+
553
+ void Win32Application::unregister_global_hotkey(const std::string& id) {
554
+ auto it = hotkey_name_to_id_.find(id);
555
+ if (it == hotkey_name_to_id_.end()) {
556
+ return;
557
+ }
558
+
559
+ UnregisterHotKey(hwnd_, it->second);
560
+ hotkey_ids_.erase(it->second);
561
+ hotkey_name_to_id_.erase(it);
562
+ }
563
+
564
+ void Win32Application::clear_global_hotkeys() {
565
+ for (const auto& entry : hotkey_ids_) {
566
+ UnregisterHotKey(hwnd_, entry.first);
567
+ }
568
+ hotkey_ids_.clear();
569
+ hotkey_name_to_id_.clear();
570
+ }
571
+
572
+ } // namespace wpp
@@ -0,0 +1,83 @@
1
+ // ============================================================================
2
+ // app_win32.h — Win32 Application implementation
3
+ // ============================================================================
4
+
5
+ #pragma once
6
+
7
+ #define WIN32_LEAN_AND_MEAN
8
+ #define NOMINMAX
9
+ #include <windows.h>
10
+ #include <map>
11
+ #include <memory>
12
+ #include <functional>
13
+ #include <mutex>
14
+ #include <queue>
15
+ #include <string>
16
+ #include "window_win32.h"
17
+ #include "tray_win32.h"
18
+ #include <windowpp/windowpp.h>
19
+
20
+ namespace wpp {
21
+
22
+ class Win32Application : public Application {
23
+ public:
24
+ Win32Application(const AppConfig& config);
25
+ ~Win32Application() override;
26
+
27
+ Window* create_window(const WindowConfig& config) override;
28
+ Window* get_window(const std::string& id) override;
29
+ std::vector<Window*> all_windows() override;
30
+
31
+ void run() override;
32
+ void quit() override;
33
+ void set_tray(const TrayConfig& config) override;
34
+ void remove_tray() override;
35
+
36
+ std::vector<MonitorInfo> monitors() override;
37
+ MonitorInfo primary_monitor() override;
38
+
39
+ std::string clipboard_text() override;
40
+ void set_clipboard_text(const std::string& text) override;
41
+
42
+ void send_message(const std::string& window_id,
43
+ const std::string& event,
44
+ const std::string& payload) override;
45
+ void on_message(const std::string& window_id, MessageHandler handler) override;
46
+
47
+ TimerId set_timeout(uint32_t ms, std::function<void()> callback) override;
48
+ TimerId set_interval(uint32_t ms, std::function<void()> callback) override;
49
+ void clear_timer(TimerId id) override;
50
+
51
+ void dispatch(std::function<void()> fn) override;
52
+
53
+ HWND get_hwnd() const { return hwnd_; }
54
+ void set_hwnd(HWND h) { hwnd_ = h; }
55
+
56
+ private:
57
+ AppConfig config_;
58
+ HWND hwnd_ = nullptr;
59
+ ATOM class_atom_ = 0;
60
+ std::map<std::string, std::unique_ptr<Win32Window>> windows_;
61
+ std::map<HWND, std::string> hwnd_to_id_;
62
+ std::unique_ptr<Win32Tray> tray_;
63
+ std::map<std::string, std::vector<MessageHandler>> message_handlers_;
64
+ bool running_ = false;
65
+ bool should_quit_ = false;
66
+ bool shutting_down_ = false;
67
+ uint64_t next_timer_id_ = 1;
68
+ std::mutex dispatch_mutex_;
69
+ std::queue<std::function<void()>> dispatch_queue_;
70
+
71
+ static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
72
+ LRESULT handle_message(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
73
+ Win32Window* get_window_by_hwnd(HWND hwnd);
74
+ bool register_global_hotkey(const std::string& id, const input::Shortcut& shortcut, std::string* error);
75
+ void unregister_global_hotkey(const std::string& id);
76
+ void clear_global_hotkeys();
77
+
78
+ std::map<int, std::string> hotkey_ids_;
79
+ std::map<std::string, int> hotkey_name_to_id_;
80
+ int next_hotkey_id_ = 1;
81
+ };
82
+
83
+ } // namespace wpp
@@ -0,0 +1,57 @@
1
+ // ============================================================================
2
+ // tray_win32.cpp — Win32 System Tray implementation
3
+ // ============================================================================
4
+
5
+ #define WIN32_LEAN_AND_MEAN
6
+ #define NOMINMAX
7
+ #include "tray_win32.h"
8
+ #include "win32_helpers.h"
9
+
10
+ namespace wpp {
11
+
12
+ Win32Tray::Win32Tray(const TrayConfig& config, HWND app_hwnd)
13
+ : config_(config), app_hwnd_(app_hwnd) {
14
+
15
+ nid_.cbSize = sizeof(NOTIFYICONDATAW);
16
+ nid_.hWnd = app_hwnd_;
17
+ nid_.uID = 1;
18
+ nid_.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
19
+ nid_.uCallbackMessage = WM_USER;
20
+
21
+ if (!config.icon_path.empty()) {
22
+ nid_.hIcon = (HICON)LoadImageW(nullptr,
23
+ win32::to_wide(config.icon_path).c_str(),
24
+ IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
25
+ }
26
+
27
+ if (!nid_.hIcon) {
28
+ nid_.hIcon = LoadIconW(nullptr, IDI_APPLICATION);
29
+ }
30
+
31
+ wcscpy_s(nid_.szTip, win32::to_wide(config.tooltip).c_str());
32
+
33
+ Shell_NotifyIconW(NIM_ADD, &nid_);
34
+ update_menu();
35
+ }
36
+
37
+ Win32Tray::~Win32Tray() {
38
+ Shell_NotifyIconW(NIM_DELETE, &nid_);
39
+ if (menu_) DestroyMenu(menu_);
40
+ }
41
+
42
+ void Win32Tray::update_menu() {
43
+ if (menu_) DestroyMenu(menu_);
44
+ menu_ = CreatePopupMenu();
45
+
46
+ for (size_t i = 0; i < config_.menu.size(); ++i) {
47
+ const auto& item = config_.menu[i];
48
+ if (item.is_separator) {
49
+ AppendMenuW(menu_, MF_SEPARATOR, 0, nullptr);
50
+ } else {
51
+ AppendMenuW(menu_, MF_STRING, (UINT)i,
52
+ win32::to_wide(item.label).c_str());
53
+ }
54
+ }
55
+ }
56
+
57
+ } // namespace wpp