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,514 @@
1
+ #pragma once
2
+
3
+ #include <condition_variable>
4
+ #include <functional>
5
+ #include <future>
6
+ #include <mutex>
7
+ #include <stdexcept>
8
+ #include <string>
9
+ #include <thread>
10
+ #include <utility>
11
+ #include <vector>
12
+
13
+ #include <windowpp/windowpp.h>
14
+
15
+ #include "../FileSystem/filesystem_json.h"
16
+
17
+ namespace wpp::platform_api {
18
+
19
+ using EvalFn = std::function<void(const std::string& js)>;
20
+ using DispatchFn = std::function<void(std::function<void()>)>;
21
+ using CreateWindowFn = std::function<void(const std::string& url, const std::string& title, int width, int height)>;
22
+
23
+ class PlatformBridge {
24
+ public:
25
+ PlatformBridge(EvalFn eval_fn,
26
+ DispatchFn dispatch_fn,
27
+ Application& app,
28
+ AppConfig app_config,
29
+ std::string current_window_id)
30
+ : eval_(std::move(eval_fn)),
31
+ dispatch_(std::move(dispatch_fn)),
32
+ app_(app),
33
+ app_config_(std::move(app_config)),
34
+ current_window_id_(std::move(current_window_id)) {}
35
+
36
+ void set_create_window_fn(CreateWindowFn fn) {
37
+ create_window_fn_ = std::move(fn);
38
+ }
39
+
40
+ ~PlatformBridge() {
41
+ {
42
+ std::lock_guard<std::mutex> lock(clipboard_mutex_);
43
+ clipboard_stop_ = true;
44
+ }
45
+ clipboard_cv_.notify_all();
46
+ if (clipboard_thread_.joinable()) {
47
+ clipboard_thread_.join();
48
+ }
49
+ }
50
+
51
+ void dispatch(const std::string& raw) {
52
+ std::string id;
53
+ std::string method;
54
+ std::vector<wpp::fs::JsonValue> params;
55
+
56
+ try {
57
+ auto env = wpp::fs::JsonValue::parse(raw);
58
+ id = env["id"].as_string();
59
+ method = env["method"].as_string();
60
+ params = env["params"].as_array();
61
+ } catch (...) {
62
+ return;
63
+ }
64
+
65
+ const bool is_app_method = method.rfind("app:", 0) == 0;
66
+ const bool is_window_method = method.rfind("window:", 0) == 0;
67
+ if (!is_app_method && !is_window_method) {
68
+ return;
69
+ }
70
+
71
+ auto reply_ok = [this, &id](const std::string& json_result) {
72
+ eval_("window.__response__(" + wpp::fs::JsonValue::to_js_argument(id) + "," + json_result + ")");
73
+ };
74
+
75
+ auto reply_err = [this, &id](const std::string& msg) {
76
+ eval_(
77
+ "window.__response__(" + wpp::fs::JsonValue::to_js_argument(id) + ",null," +
78
+ wpp::fs::JsonValue::to_js_argument(msg) + ")");
79
+ };
80
+
81
+ try {
82
+ if (method == "app:getInfo") {
83
+ reply_ok(app_info_to_json());
84
+
85
+ } else if (method == "app:getMonitors") {
86
+ reply_ok(monitors_to_json(app_.monitors()));
87
+
88
+ } else if (method == "app:getPrimaryMonitor") {
89
+ reply_ok(monitor_to_json(app_.primary_monitor()));
90
+
91
+ } else if (method == "app:getClipboardText") {
92
+ reply_ok(wpp::fs::JsonValue::dump_value(app_.clipboard_text()));
93
+
94
+ } else if (method == "app:setClipboardText") {
95
+ if (params.empty()) {
96
+ throw std::runtime_error("Expected clipboard text");
97
+ }
98
+ app_.set_clipboard_text(params[0].as_string());
99
+ publish_clipboard_change(params[0].as_string(), true);
100
+ reply_ok("null");
101
+
102
+ } else if (method == "app:setEventStreamEnabled") {
103
+ app_stream_enabled_ = !params.empty() && params[0].as_bool();
104
+ if (app_stream_enabled_) {
105
+ ensure_clipboard_poller_started();
106
+ // Called directly on the main (IPC) thread — do NOT use
107
+ // read_clipboard_text_on_ui_thread() here; that dispatches back to
108
+ // this same thread and would deadlock.
109
+ publish_clipboard_change(app_.clipboard_text(), false);
110
+ }
111
+ reply_ok("null");
112
+
113
+ } else if (method == "window:setEventStreamEnabled") {
114
+ stream_enabled_ = !params.empty() && params[0].as_bool();
115
+ reply_ok("null");
116
+
117
+ } else if (method == "window:getCurrent") {
118
+ reply_ok(window_to_json(require_window(current_window_id_)));
119
+
120
+ } else if (method == "window:get") {
121
+ reply_ok(window_to_json(require_window(resolve_window_id(params, 0))));
122
+
123
+ } else if (method == "window:list") {
124
+ reply_ok(windows_to_json(app_.all_windows()));
125
+
126
+ } else if (method == "window:setTitle") {
127
+ auto* window = require_window(resolve_window_id(params, 1));
128
+ if (params.empty()) {
129
+ throw std::runtime_error("Expected title");
130
+ }
131
+ window->set_title(params[0].as_string());
132
+ reply_ok(window_to_json(window));
133
+
134
+ } else if (method == "window:setSize") {
135
+ auto* window = require_window(resolve_window_id(params, 2));
136
+ if (params.size() < 2) {
137
+ throw std::runtime_error("Expected width and height");
138
+ }
139
+ window->set_size(params[0].as_int(), params[1].as_int());
140
+ reply_ok(window_to_json(window));
141
+
142
+ } else if (method == "window:setPosition") {
143
+ auto* window = require_window(resolve_window_id(params, 2));
144
+ if (params.size() < 2) {
145
+ throw std::runtime_error("Expected x and y");
146
+ }
147
+ window->set_position(params[0].as_int(), params[1].as_int());
148
+ reply_ok(window_to_json(window));
149
+
150
+ } else if (method == "window:setMinSize") {
151
+ auto* window = require_window(resolve_window_id(params, 2));
152
+ if (params.size() < 2) {
153
+ throw std::runtime_error("Expected width and height");
154
+ }
155
+ window->set_min_size(params[0].as_int(), params[1].as_int());
156
+ reply_ok(window_to_json(window));
157
+
158
+ } else if (method == "window:setMaxSize") {
159
+ auto* window = require_window(resolve_window_id(params, 2));
160
+ if (params.size() < 2) {
161
+ throw std::runtime_error("Expected width and height");
162
+ }
163
+ window->set_max_size(params[0].as_int(), params[1].as_int());
164
+ reply_ok(window_to_json(window));
165
+
166
+ } else if (method == "window:setResizable") {
167
+ auto* window = require_window(resolve_window_id(params, 1));
168
+ if (params.empty()) {
169
+ throw std::runtime_error("Expected boolean value");
170
+ }
171
+ window->set_resizable(params[0].as_bool());
172
+ reply_ok(window_to_json(window));
173
+
174
+ } else if (method == "window:setAlwaysOnTop") {
175
+ auto* window = require_window(resolve_window_id(params, 1));
176
+ if (params.empty()) {
177
+ throw std::runtime_error("Expected boolean value");
178
+ }
179
+ window->set_always_on_top(params[0].as_bool());
180
+ reply_ok(window_to_json(window));
181
+
182
+ } else if (method == "window:setOpacity") {
183
+ auto* window = require_window(resolve_window_id(params, 1));
184
+ if (params.empty()) {
185
+ throw std::runtime_error("Expected opacity value");
186
+ }
187
+ window->set_opacity(static_cast<float>(params[0].as_double()));
188
+ reply_ok(window_to_json(window));
189
+
190
+ } else if (method == "window:show") {
191
+ auto* window = require_window(resolve_window_id(params, 0));
192
+ window->show();
193
+ reply_ok(window_to_json(window));
194
+
195
+ } else if (method == "window:hide") {
196
+ auto* window = require_window(resolve_window_id(params, 0));
197
+ window->hide();
198
+ reply_ok(window_to_json(window));
199
+
200
+ } else if (method == "window:close") {
201
+ auto* window = require_window(resolve_window_id(params, 0));
202
+ window->close();
203
+ reply_ok("null");
204
+
205
+ } else if (method == "window:focus") {
206
+ auto* window = require_window(resolve_window_id(params, 0));
207
+ window->focus();
208
+ reply_ok(window_to_json(window));
209
+
210
+ } else if (method == "window:minimize") {
211
+ auto* window = require_window(resolve_window_id(params, 0));
212
+ window->minimize();
213
+ reply_ok(window_to_json(window));
214
+
215
+ } else if (method == "window:maximize") {
216
+ auto* window = require_window(resolve_window_id(params, 0));
217
+ window->maximize();
218
+ reply_ok(window_to_json(window));
219
+
220
+ } else if (method == "window:restore") {
221
+ auto* window = require_window(resolve_window_id(params, 0));
222
+ window->restore();
223
+ reply_ok(window_to_json(window));
224
+
225
+ } else if (method == "window:fullscreen") {
226
+ auto* window = require_window(resolve_window_id(params, 0));
227
+ window->fullscreen();
228
+ reply_ok(window_to_json(window));
229
+
230
+ } else if (method == "window:center") {
231
+ auto* window = require_window(resolve_window_id(params, 0));
232
+ window->center();
233
+ reply_ok(window_to_json(window));
234
+
235
+ } else if (method == "window:create") {
236
+ if (params.empty()) {
237
+ throw std::runtime_error("Expected URL");
238
+ }
239
+ std::string url = params[0].as_string();
240
+ std::string title = params.size() > 1 ? params[1].as_string() : "New Window";
241
+ int width = params.size() > 2 ? params[2].as_int() : 800;
242
+ int height = params.size() > 3 ? params[3].as_int() : 600;
243
+ if (!create_window_fn_) {
244
+ throw std::runtime_error("window:create is not configured");
245
+ }
246
+ dispatch_([this, url, title, width, height]() {
247
+ create_window_fn_(url, title, width, height);
248
+ });
249
+ reply_ok("null");
250
+
251
+ } else {
252
+ throw std::runtime_error("Unsupported platform API method: " + method);
253
+ }
254
+ } catch (const std::exception& e) {
255
+ reply_err(e.what());
256
+ } catch (...) {
257
+ reply_err("Unknown error in platform bridge");
258
+ }
259
+ }
260
+
261
+ void emit_window_event(const std::string& event_name,
262
+ const std::string& window_id,
263
+ const std::string& detail_json = "null") {
264
+ if (!stream_enabled_) {
265
+ return;
266
+ }
267
+
268
+ std::string payload = "{";
269
+ payload += "\"name\":" + wpp::fs::JsonValue::dump_value(event_name);
270
+ payload += ",\"windowId\":" + wpp::fs::JsonValue::dump_value(window_id);
271
+ if (auto* window = app_.get_window(window_id)) {
272
+ payload += ",\"window\":" + window_to_json(window);
273
+ }
274
+ payload += ",\"detail\":" + detail_json;
275
+ payload += "}";
276
+ emit_event("window", payload);
277
+ }
278
+
279
+ void emit_resize_event(const std::string& window_id, const ResizeEvent& event) {
280
+ emit_window_event(window_id.empty() ? "resize" : "resize", window_id, resize_event_to_json(event));
281
+ }
282
+
283
+ void emit_move_event(const std::string& window_id, const MoveEvent& event) {
284
+ emit_window_event("move", window_id, move_event_to_json(event));
285
+ }
286
+
287
+ private:
288
+ static std::string point_to_json(Point point) {
289
+ return std::string("{\"x\":") + std::to_string(point.x) + ",\"y\":" + std::to_string(point.y) + "}";
290
+ }
291
+
292
+ static std::string size_to_json(Size size) {
293
+ return std::string("{\"width\":") + std::to_string(size.width) + ",\"height\":" + std::to_string(size.height) + "}";
294
+ }
295
+
296
+ static std::string rect_to_json(Rect rect) {
297
+ return std::string("{\"x\":") + std::to_string(rect.x) +
298
+ ",\"y\":" + std::to_string(rect.y) +
299
+ ",\"width\":" + std::to_string(rect.width) +
300
+ ",\"height\":" + std::to_string(rect.height) + "}";
301
+ }
302
+
303
+ static std::string state_to_string(WindowState state) {
304
+ switch (state) {
305
+ case WindowState::Normal: return "normal";
306
+ case WindowState::Minimized: return "minimized";
307
+ case WindowState::Maximized: return "maximized";
308
+ case WindowState::Fullscreen: return "fullscreen";
309
+ case WindowState::Hidden: return "hidden";
310
+ default: return "normal";
311
+ }
312
+ }
313
+
314
+ static std::string monitor_to_json(const MonitorInfo& monitor) {
315
+ std::string json = "{";
316
+ json += "\"name\":" + wpp::fs::JsonValue::dump_value(monitor.name);
317
+ json += ",\"bounds\":" + rect_to_json(monitor.bounds);
318
+ json += ",\"workArea\":" + rect_to_json(monitor.work_area);
319
+ json += ",\"scaleFactor\":" + std::to_string(monitor.scale_factor);
320
+ json += ",\"isPrimary\":" + std::string(monitor.is_primary ? "true" : "false");
321
+ json += "}";
322
+ return json;
323
+ }
324
+
325
+ static std::string monitors_to_json(const std::vector<MonitorInfo>& monitors) {
326
+ std::string json = "[";
327
+ for (size_t index = 0; index < monitors.size(); ++index) {
328
+ if (index > 0) {
329
+ json += ',';
330
+ }
331
+ json += monitor_to_json(monitors[index]);
332
+ }
333
+ json += "]";
334
+ return json;
335
+ }
336
+
337
+ std::string app_info_to_json() const {
338
+ std::string json = "{";
339
+ json += "\"name\":" + wpp::fs::JsonValue::dump_value(app_config_.name);
340
+ json += ",\"version\":" + wpp::fs::JsonValue::dump_value(app_config_.version);
341
+ json += ",\"identifier\":" + wpp::fs::JsonValue::dump_value(app_config_.identifier);
342
+ json += "}";
343
+ return json;
344
+ }
345
+
346
+ std::string app_event_to_json(const std::string& name, const std::string& clipboard_text) const {
347
+ std::string json = "{";
348
+ json += "\"name\":" + wpp::fs::JsonValue::dump_value(name);
349
+ json += ",\"clipboardText\":" + wpp::fs::JsonValue::dump_value(clipboard_text);
350
+ json += "}";
351
+ return json;
352
+ }
353
+
354
+ std::string window_to_json(Window* window) const {
355
+ if (!window) {
356
+ return "null";
357
+ }
358
+
359
+ std::string json = "{";
360
+ json += "\"id\":" + wpp::fs::JsonValue::dump_value(window->id());
361
+ json += ",\"title\":" + wpp::fs::JsonValue::dump_value(window->title());
362
+ json += ",\"size\":" + size_to_json(window->size());
363
+ json += ",\"position\":" + point_to_json(window->position());
364
+ json += ",\"state\":" + wpp::fs::JsonValue::dump_value(state_to_string(window->state()));
365
+ json += ",\"visible\":" + std::string(window->is_visible() ? "true" : "false");
366
+ json += ",\"focused\":" + std::string(window->is_focused() ? "true" : "false");
367
+ json += ",\"scaleFactor\":" + std::to_string(window->scale_factor());
368
+ json += "}";
369
+ return json;
370
+ }
371
+
372
+ std::string windows_to_json(const std::vector<Window*>& windows) const {
373
+ std::string json = "[";
374
+ for (size_t index = 0; index < windows.size(); ++index) {
375
+ if (index > 0) {
376
+ json += ',';
377
+ }
378
+ json += window_to_json(windows[index]);
379
+ }
380
+ json += "]";
381
+ return json;
382
+ }
383
+
384
+ static std::string resize_event_to_json(const ResizeEvent& event) {
385
+ return std::string("{\"oldSize\":") + size_to_json(event.old_size) +
386
+ ",\"newSize\":" + size_to_json(event.new_size) + "}";
387
+ }
388
+
389
+ static std::string move_event_to_json(const MoveEvent& event) {
390
+ return std::string("{\"oldPosition\":") + point_to_json(event.old_position) +
391
+ ",\"newPosition\":" + point_to_json(event.new_position) + "}";
392
+ }
393
+
394
+ std::string resolve_window_id(const std::vector<wpp::fs::JsonValue>& params, size_t index) const {
395
+ if (params.size() > index) {
396
+ return params[index].as_string();
397
+ }
398
+ return current_window_id_;
399
+ }
400
+
401
+ Window* require_window(const std::string& id) const {
402
+ auto* window = app_.get_window(id.empty() ? current_window_id_ : id);
403
+ if (!window) {
404
+ throw std::runtime_error("Window not found: " + (id.empty() ? current_window_id_ : id));
405
+ }
406
+ return window;
407
+ }
408
+
409
+ void emit_event(const std::string& event_name, const std::string& json_payload) {
410
+ std::string script = "window.__wpp_platform_event__ && window.__wpp_platform_event__(" +
411
+ wpp::fs::JsonValue::to_js_argument(event_name) + "," +
412
+ json_payload + ")";
413
+ if (dispatch_) {
414
+ dispatch_([eval = eval_, script = std::move(script)]() {
415
+ eval(script);
416
+ });
417
+ return;
418
+ }
419
+ eval_(script);
420
+ }
421
+
422
+ void ensure_clipboard_poller_started() {
423
+ std::lock_guard<std::mutex> lock(clipboard_mutex_);
424
+ if (clipboard_poller_started_) {
425
+ return;
426
+ }
427
+ clipboard_poller_started_ = true;
428
+ clipboard_thread_ = std::thread([this]() {
429
+ clipboard_poll_loop();
430
+ });
431
+ }
432
+
433
+ void clipboard_poll_loop() {
434
+ std::unique_lock<std::mutex> lock(clipboard_mutex_);
435
+ while (!clipboard_stop_) {
436
+ clipboard_cv_.wait_for(lock, std::chrono::milliseconds(800));
437
+ if (clipboard_stop_) {
438
+ break;
439
+ }
440
+ if (!app_stream_enabled_) {
441
+ continue;
442
+ }
443
+ lock.unlock();
444
+ const auto clipboard_text = read_clipboard_text_on_ui_thread();
445
+ publish_clipboard_change(clipboard_text, false);
446
+ lock.lock();
447
+ }
448
+ }
449
+
450
+ std::string read_clipboard_text_on_ui_thread() {
451
+ if (!dispatch_) {
452
+ return app_.clipboard_text();
453
+ }
454
+
455
+ auto promise = std::make_shared<std::promise<std::string>>();
456
+ auto future = promise->get_future();
457
+ dispatch_([this, promise]() {
458
+ try {
459
+ promise->set_value(app_.clipboard_text());
460
+ } catch (...) {
461
+ try {
462
+ promise->set_exception(std::current_exception());
463
+ } catch (...) {
464
+ }
465
+ }
466
+ });
467
+
468
+ try {
469
+ return future.get();
470
+ } catch (...) {
471
+ return {};
472
+ }
473
+ }
474
+
475
+ void publish_clipboard_change(const std::string& clipboard_text, bool force_emit) {
476
+ if (!app_stream_enabled_) {
477
+ return;
478
+ }
479
+
480
+ bool should_emit = force_emit;
481
+ {
482
+ std::lock_guard<std::mutex> lock(clipboard_mutex_);
483
+ if (!clipboard_initialized_ || clipboard_last_text_ != clipboard_text) {
484
+ clipboard_initialized_ = true;
485
+ clipboard_last_text_ = clipboard_text;
486
+ should_emit = true;
487
+ }
488
+ }
489
+
490
+ if (!should_emit) {
491
+ return;
492
+ }
493
+
494
+ emit_event("app", app_event_to_json("clipboardChanged", clipboard_text));
495
+ }
496
+
497
+ EvalFn eval_;
498
+ DispatchFn dispatch_;
499
+ CreateWindowFn create_window_fn_;
500
+ Application& app_;
501
+ AppConfig app_config_;
502
+ std::string current_window_id_;
503
+ std::mutex clipboard_mutex_;
504
+ std::condition_variable clipboard_cv_;
505
+ std::thread clipboard_thread_;
506
+ bool clipboard_stop_ = false;
507
+ bool clipboard_poller_started_ = false;
508
+ bool clipboard_initialized_ = false;
509
+ bool app_stream_enabled_ = false;
510
+ std::string clipboard_last_text_;
511
+ bool stream_enabled_ = false;
512
+ };
513
+
514
+ } // namespace wpp::platform_api
@@ -0,0 +1,33 @@
1
+ // ============================================================================
2
+ // platform_factory.cpp — Creates platform-specific Application instance
3
+ // ============================================================================
4
+
5
+ #include "platform_factory.h"
6
+
7
+ #if defined(WIN32)
8
+ #include "win32/app_win32.h"
9
+ #elif defined(__APPLE__)
10
+ #include "macos/app_macos.h"
11
+ #elif defined(__linux__)
12
+ #include "linux/app_linux.h"
13
+ #else
14
+ #error "Unsupported platform"
15
+ #endif
16
+
17
+ namespace wpp {
18
+
19
+ #if defined(WIN32)
20
+ std::unique_ptr<Application> PlatformFactory::create_application(const AppConfig& config) {
21
+ return std::make_unique<Win32Application>(config);
22
+ }
23
+ #elif defined(__APPLE__)
24
+ std::unique_ptr<Application> PlatformFactory::create_application(const AppConfig& config) {
25
+ return std::make_unique<MacOSApplication>(config);
26
+ }
27
+ #elif defined(__linux__)
28
+ std::unique_ptr<Application> PlatformFactory::create_application(const AppConfig& config) {
29
+ return std::make_unique<LinuxApplication>(config);
30
+ }
31
+ #endif
32
+
33
+ } // namespace wpp
@@ -0,0 +1,19 @@
1
+ // ============================================================================
2
+ // platform_factory.h — Creates platform-specific Application instance
3
+ // ============================================================================
4
+
5
+ #pragma once
6
+
7
+ #include <memory>
8
+
9
+ namespace wpp {
10
+
11
+ class Application;
12
+ class AppConfig;
13
+
14
+ class PlatformFactory {
15
+ public:
16
+ static std::unique_ptr<Application> create_application(const AppConfig& config);
17
+ };
18
+
19
+ } // namespace wpp