basalt-core 0.0.0-rc5

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 (325) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cli/basalt.d.ts +3 -0
  3. package/dist/cli/basalt.d.ts.map +1 -0
  4. package/dist/cli/basalt.js +47 -0
  5. package/dist/cli/basalt.js.map +1 -0
  6. package/dist/cli/bundle-cli.d.ts +12 -0
  7. package/dist/cli/bundle-cli.d.ts.map +1 -0
  8. package/dist/cli/bundle-cli.js +124 -0
  9. package/dist/cli/bundle-cli.js.map +1 -0
  10. package/dist/cli/bundleWithAssets.d.ts +79 -0
  11. package/dist/cli/bundleWithAssets.d.ts.map +1 -0
  12. package/dist/cli/bundleWithAssets.js +198 -0
  13. package/dist/cli/bundleWithAssets.js.map +1 -0
  14. package/dist/cli/desktop.d.ts +339 -0
  15. package/dist/cli/desktop.d.ts.map +1 -0
  16. package/dist/cli/desktop.js +1029 -0
  17. package/dist/cli/desktop.js.map +1 -0
  18. package/dist/cli/doctor.d.ts +31 -0
  19. package/dist/cli/doctor.d.ts.map +1 -0
  20. package/dist/cli/doctor.js +283 -0
  21. package/dist/cli/doctor.js.map +1 -0
  22. package/dist/cli/init.d.ts +77 -0
  23. package/dist/cli/init.d.ts.map +1 -0
  24. package/dist/cli/init.js +442 -0
  25. package/dist/cli/init.js.map +1 -0
  26. package/dist/cli/metro.d.ts +49 -0
  27. package/dist/cli/metro.d.ts.map +1 -0
  28. package/dist/cli/metro.js +148 -0
  29. package/dist/cli/metro.js.map +1 -0
  30. package/dist/cli/packageApp.d.ts +102 -0
  31. package/dist/cli/packageApp.d.ts.map +1 -0
  32. package/dist/cli/packageApp.js +319 -0
  33. package/dist/cli/packageApp.js.map +1 -0
  34. package/dist/cli/steps.d.ts +34 -0
  35. package/dist/cli/steps.d.ts.map +1 -0
  36. package/dist/cli/steps.js +53 -0
  37. package/dist/cli/steps.js.map +1 -0
  38. package/dist/metro-config.d.ts +168 -0
  39. package/dist/metro-config.d.ts.map +1 -0
  40. package/dist/metro-config.js +671 -0
  41. package/dist/metro-config.js.map +1 -0
  42. package/dist/react-native.config.d.ts +7 -0
  43. package/dist/react-native.config.d.ts.map +1 -0
  44. package/dist/react-native.config.js +80 -0
  45. package/dist/react-native.config.js.map +1 -0
  46. package/dist/src/DragSource.d.ts +52 -0
  47. package/dist/src/DragSource.d.ts.map +1 -0
  48. package/dist/src/DragSource.js +97 -0
  49. package/dist/src/DragSource.js.map +1 -0
  50. package/dist/src/DropTarget.d.ts +58 -0
  51. package/dist/src/DropTarget.d.ts.map +1 -0
  52. package/dist/src/DropTarget.js +131 -0
  53. package/dist/src/DropTarget.js.map +1 -0
  54. package/dist/src/KeyHandler.d.ts +57 -0
  55. package/dist/src/KeyHandler.d.ts.map +1 -0
  56. package/dist/src/KeyHandler.js +48 -0
  57. package/dist/src/KeyHandler.js.map +1 -0
  58. package/dist/src/Menu.d.ts +72 -0
  59. package/dist/src/Menu.d.ts.map +1 -0
  60. package/dist/src/Menu.js +179 -0
  61. package/dist/src/Menu.js.map +1 -0
  62. package/dist/src/TitleBar.d.ts +75 -0
  63. package/dist/src/TitleBar.d.ts.map +1 -0
  64. package/dist/src/TitleBar.js +236 -0
  65. package/dist/src/TitleBar.js.map +1 -0
  66. package/dist/src/Window.d.ts +87 -0
  67. package/dist/src/Window.d.ts.map +1 -0
  68. package/dist/src/Window.js +248 -0
  69. package/dist/src/Window.js.map +1 -0
  70. package/dist/src/closeRequest.d.ts +55 -0
  71. package/dist/src/closeRequest.d.ts.map +1 -0
  72. package/dist/src/closeRequest.js +134 -0
  73. package/dist/src/closeRequest.js.map +1 -0
  74. package/dist/src/index.d.ts +45 -0
  75. package/dist/src/index.d.ts.map +1 -0
  76. package/dist/src/index.js +92 -0
  77. package/dist/src/index.js.map +1 -0
  78. package/dist/src/overrides/Alert.d.ts +69 -0
  79. package/dist/src/overrides/Alert.d.ts.map +1 -0
  80. package/dist/src/overrides/Alert.js +112 -0
  81. package/dist/src/overrides/Alert.js.map +1 -0
  82. package/dist/src/overrides/BaseViewConfig.d.ts +28 -0
  83. package/dist/src/overrides/BaseViewConfig.d.ts.map +1 -0
  84. package/dist/src/overrides/BaseViewConfig.js +72 -0
  85. package/dist/src/overrides/BaseViewConfig.js.map +1 -0
  86. package/dist/src/overrides/Platform.linux.d.ts +10 -0
  87. package/dist/src/overrides/Platform.linux.d.ts.map +1 -0
  88. package/dist/src/overrides/Platform.linux.js +15 -0
  89. package/dist/src/overrides/Platform.linux.js.map +1 -0
  90. package/dist/src/overrides/Platform.macos.d.ts +10 -0
  91. package/dist/src/overrides/Platform.macos.d.ts.map +1 -0
  92. package/dist/src/overrides/Platform.macos.js +15 -0
  93. package/dist/src/overrides/Platform.macos.js.map +1 -0
  94. package/dist/src/overrides/Platform.windows.d.ts +10 -0
  95. package/dist/src/overrides/Platform.windows.d.ts.map +1 -0
  96. package/dist/src/overrides/Platform.windows.js +15 -0
  97. package/dist/src/overrides/Platform.windows.js.map +1 -0
  98. package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts +24 -0
  99. package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts.map +1 -0
  100. package/dist/src/overrides/ReactDevToolsSettingsManager.js +30 -0
  101. package/dist/src/overrides/ReactDevToolsSettingsManager.js.map +1 -0
  102. package/dist/src/overrides/RefreshControl.d.ts +52 -0
  103. package/dist/src/overrides/RefreshControl.d.ts.map +1 -0
  104. package/dist/src/overrides/RefreshControl.js +92 -0
  105. package/dist/src/overrides/RefreshControl.js.map +1 -0
  106. package/dist/src/overrides/ScrollView.d.ts +39 -0
  107. package/dist/src/overrides/ScrollView.d.ts.map +1 -0
  108. package/dist/src/overrides/ScrollView.js +59 -0
  109. package/dist/src/overrides/ScrollView.js.map +1 -0
  110. package/dist/src/overrides/Share.d.ts +96 -0
  111. package/dist/src/overrides/Share.d.ts.map +1 -0
  112. package/dist/src/overrides/Share.js +97 -0
  113. package/dist/src/overrides/Share.js.map +1 -0
  114. package/dist/src/overrides/TextInput.d.ts +81 -0
  115. package/dist/src/overrides/TextInput.d.ts.map +1 -0
  116. package/dist/src/overrides/TextInput.js +213 -0
  117. package/dist/src/overrides/TextInput.js.map +1 -0
  118. package/dist/src/overrides/View.d.ts +36 -0
  119. package/dist/src/overrides/View.d.ts.map +1 -0
  120. package/dist/src/overrides/View.js +99 -0
  121. package/dist/src/overrides/View.js.map +1 -0
  122. package/dist/src/overrides/createPlatform.d.ts +32 -0
  123. package/dist/src/overrides/createPlatform.d.ts.map +1 -0
  124. package/dist/src/overrides/createPlatform.js +84 -0
  125. package/dist/src/overrides/createPlatform.js.map +1 -0
  126. package/dist/src/overrides/setUpDeveloperTools.d.ts +28 -0
  127. package/dist/src/overrides/setUpDeveloperTools.d.ts.map +1 -0
  128. package/dist/src/overrides/setUpDeveloperTools.js +84 -0
  129. package/dist/src/overrides/setUpDeveloperTools.js.map +1 -0
  130. package/dist/src/overrides/setUpXHR.d.ts +46 -0
  131. package/dist/src/overrides/setUpXHR.d.ts.map +1 -0
  132. package/dist/src/overrides/setUpXHR.js +236 -0
  133. package/dist/src/overrides/setUpXHR.js.map +1 -0
  134. package/dist/src/quitRequest.d.ts +55 -0
  135. package/dist/src/quitRequest.d.ts.map +1 -0
  136. package/dist/src/quitRequest.js +121 -0
  137. package/dist/src/quitRequest.js.map +1 -0
  138. package/dist/src/resourcePath.d.ts +18 -0
  139. package/dist/src/resourcePath.d.ts.map +1 -0
  140. package/dist/src/resourcePath.js +31 -0
  141. package/dist/src/resourcePath.js.map +1 -0
  142. package/dist/src/responseBody.d.ts +76 -0
  143. package/dist/src/responseBody.d.ts.map +1 -0
  144. package/dist/src/responseBody.js +94 -0
  145. package/dist/src/responseBody.js.map +1 -0
  146. package/dist/src/responseHeaders.d.ts +40 -0
  147. package/dist/src/responseHeaders.d.ts.map +1 -0
  148. package/dist/src/responseHeaders.js +75 -0
  149. package/dist/src/responseHeaders.js.map +1 -0
  150. package/dist/src/titleBarState.d.ts +43 -0
  151. package/dist/src/titleBarState.d.ts.map +1 -0
  152. package/dist/src/titleBarState.js +77 -0
  153. package/dist/src/titleBarState.js.map +1 -0
  154. package/dist/src/useContextMenu.d.ts +113 -0
  155. package/dist/src/useContextMenu.d.ts.map +1 -0
  156. package/dist/src/useContextMenu.js +109 -0
  157. package/dist/src/useContextMenu.js.map +1 -0
  158. package/dist/src/useDialog.d.ts +98 -0
  159. package/dist/src/useDialog.d.ts.map +1 -0
  160. package/dist/src/useDialog.js +112 -0
  161. package/dist/src/useDialog.js.map +1 -0
  162. package/dist/src/useDisplays.d.ts +70 -0
  163. package/dist/src/useDisplays.d.ts.map +1 -0
  164. package/dist/src/useDisplays.js +118 -0
  165. package/dist/src/useDisplays.js.map +1 -0
  166. package/dist/src/useHandledKeys.d.ts +38 -0
  167. package/dist/src/useHandledKeys.d.ts.map +1 -0
  168. package/dist/src/useHandledKeys.js +70 -0
  169. package/dist/src/useHandledKeys.js.map +1 -0
  170. package/dist/src/useWindow.d.ts +140 -0
  171. package/dist/src/useWindow.d.ts.map +1 -0
  172. package/dist/src/useWindow.js +233 -0
  173. package/dist/src/useWindow.js.map +1 -0
  174. package/dist/tsconfig.tsbuildinfo +1 -0
  175. package/native/CMakeLists.txt +355 -0
  176. package/native/bootstrap.sh +613 -0
  177. package/native/cmake/CompilerFlags.cmake +49 -0
  178. package/native/cmake/Expo.cmake +125 -0
  179. package/native/cmake/ReactNativeCore.cmake +612 -0
  180. package/native/cmake/Reanimated.cmake +108 -0
  181. package/native/cmake/Skia.cmake +379 -0
  182. package/native/cmake/ThirdParty.cmake +290 -0
  183. package/native/cmake/Worklets.cmake +67 -0
  184. package/native/cmake/codegen/CMakeLists.txt +32 -0
  185. package/native/core/AppIdentity.cpp +85 -0
  186. package/native/core/AppIdentity.h +59 -0
  187. package/native/core/AppPaths.cpp +96 -0
  188. package/native/core/AppPaths.h +53 -0
  189. package/native/core/AppearanceModule.cpp +60 -0
  190. package/native/core/AppearanceModule.h +40 -0
  191. package/native/core/Backface.h +59 -0
  192. package/native/core/BlobModule.cpp +283 -0
  193. package/native/core/BlobModule.h +76 -0
  194. package/native/core/BlobRegistry.cpp +75 -0
  195. package/native/core/BlobRegistry.h +44 -0
  196. package/native/core/ColorScheme.cpp +105 -0
  197. package/native/core/ColorScheme.h +59 -0
  198. package/native/core/ComponentRegistry.h +30 -0
  199. package/native/core/ControlMetrics.h +37 -0
  200. package/native/core/CoreModules.cpp +592 -0
  201. package/native/core/CoreModules.h +308 -0
  202. package/native/core/DebuggingOverlay.cpp +106 -0
  203. package/native/core/DebuggingOverlay.h +65 -0
  204. package/native/core/DesktopControls.cpp +232 -0
  205. package/native/core/DesktopControls.h +170 -0
  206. package/native/core/DevBundle.cpp +108 -0
  207. package/native/core/DevBundle.h +69 -0
  208. package/native/core/DevMenu.cpp +99 -0
  209. package/native/core/DevMenu.h +75 -0
  210. package/native/core/DialogModule.cpp +150 -0
  211. package/native/core/DialogModule.h +51 -0
  212. package/native/core/DragAndDrop.cpp +45 -0
  213. package/native/core/DragAndDrop.h +225 -0
  214. package/native/core/ExpoImageComponent.cpp +79 -0
  215. package/native/core/ExpoImageComponent.h +80 -0
  216. package/native/core/ExpoModules.cpp +415 -0
  217. package/native/core/ExpoModules.h +115 -0
  218. package/native/core/ExpoRuntime.cpp +366 -0
  219. package/native/core/ExpoRuntime.h +42 -0
  220. package/native/core/FocusRing.h +36 -0
  221. package/native/core/FontRegistry.h +51 -0
  222. package/native/core/GestureHandlerModule.cpp +136 -0
  223. package/native/core/GestureHandlerModule.h +71 -0
  224. package/native/core/Gestures.cpp +533 -0
  225. package/native/core/Gestures.h +145 -0
  226. package/native/core/HoverTracker.cpp +69 -0
  227. package/native/core/HoverTracker.h +109 -0
  228. package/native/core/HttpClient.cpp +282 -0
  229. package/native/core/ImageBytes.cpp +205 -0
  230. package/native/core/ImageBytes.h +29 -0
  231. package/native/core/ImageCache.cpp +51 -0
  232. package/native/core/ImageCache.h +89 -0
  233. package/native/core/JsRuntimeAccess.cpp +70 -0
  234. package/native/core/JsRuntimeAccess.h +60 -0
  235. package/native/core/JsiPromise.h +51 -0
  236. package/native/core/KeyEvents.cpp +116 -0
  237. package/native/core/KeyEvents.h +144 -0
  238. package/native/core/LogBoxSurface.cpp +46 -0
  239. package/native/core/LogBoxSurface.h +68 -0
  240. package/native/core/MenuModel.h +117 -0
  241. package/native/core/MenuModule.cpp +209 -0
  242. package/native/core/MenuModule.h +66 -0
  243. package/native/core/MountingWalk.h +524 -0
  244. package/native/core/PackageModules.cpp.in +25 -0
  245. package/native/core/PackageModules.h +50 -0
  246. package/native/core/PlatformConstantsModule.cpp +63 -0
  247. package/native/core/PlatformConstantsModule.h +64 -0
  248. package/native/core/PlatformServices.h +213 -0
  249. package/native/core/PointerButtons.h +63 -0
  250. package/native/core/PullToRefresh.cpp +57 -0
  251. package/native/core/PullToRefresh.h +73 -0
  252. package/native/core/ReanimatedCompat.h +47 -0
  253. package/native/core/ReanimatedModule.cpp +203 -0
  254. package/native/core/ReanimatedModule.h +55 -0
  255. package/native/core/ScrollAnimation.cpp +59 -0
  256. package/native/core/ScrollAnimation.h +70 -0
  257. package/native/core/ScrollBounds.cpp +26 -0
  258. package/native/core/ScrollBounds.h +68 -0
  259. package/native/core/ScrollIndicator.cpp +50 -0
  260. package/native/core/ScrollIndicator.h +69 -0
  261. package/native/core/ScrollMomentum.cpp +71 -0
  262. package/native/core/ScrollMomentum.h +73 -0
  263. package/native/core/ScrollSnap.cpp +123 -0
  264. package/native/core/ScrollSnap.h +80 -0
  265. package/native/core/ShareFallback.cpp +101 -0
  266. package/native/core/ShareFallback.h +45 -0
  267. package/native/core/SkiaPictureViewComponent.cpp +11 -0
  268. package/native/core/SkiaPictureViewComponent.h +47 -0
  269. package/native/core/SourceCodeModule.cpp +55 -0
  270. package/native/core/SourceCodeModule.h +53 -0
  271. package/native/core/StatusBarModule.cpp +23 -0
  272. package/native/core/StatusBarModule.h +42 -0
  273. package/native/core/TestDialog.cpp +148 -0
  274. package/native/core/TestDialog.h +96 -0
  275. package/native/core/TestKeys.cpp +78 -0
  276. package/native/core/TestKeys.h +50 -0
  277. package/native/core/TestQuitFile.cpp +33 -0
  278. package/native/core/TestQuitFile.h +60 -0
  279. package/native/core/TestSettle.h +192 -0
  280. package/native/core/TitleBarRegions.h +31 -0
  281. package/native/core/TurboModuleProxy.cpp +51 -0
  282. package/native/core/TurboModuleProxy.h +46 -0
  283. package/native/core/UIManagerAccess.cpp +43 -0
  284. package/native/core/UIManagerAccess.h +43 -0
  285. package/native/core/WindowBoundsCache.cpp +319 -0
  286. package/native/core/WindowControl.h +235 -0
  287. package/native/core/WindowHost.h +158 -0
  288. package/native/core/WindowMethods.cpp +167 -0
  289. package/native/core/WindowMethods.h +84 -0
  290. package/native/core/WindowsModule.cpp +448 -0
  291. package/native/core/WindowsModule.h +133 -0
  292. package/native/core/WorkletsModule.cpp +199 -0
  293. package/native/core/WorkletsModule.h +57 -0
  294. package/native/core/portability_probe.cpp +210 -0
  295. package/native/tests/EventRecorder.h +124 -0
  296. package/native/tests/TestHarness.cpp +109 -0
  297. package/native/tests/TestHarness.h +89 -0
  298. package/native/tests/TreeDump.h +49 -0
  299. package/native/tests/test_app_identity.cpp +114 -0
  300. package/native/tests/test_app_paths.cpp +48 -0
  301. package/native/tests/test_appearance.cpp +107 -0
  302. package/native/tests/test_backface.cpp +79 -0
  303. package/native/tests/test_blobs.cpp +157 -0
  304. package/native/tests/test_controls.cpp +185 -0
  305. package/native/tests/test_devbundle.cpp +103 -0
  306. package/native/tests/test_drag_and_drop.cpp +143 -0
  307. package/native/tests/test_gestures.cpp +331 -0
  308. package/native/tests/test_hover.cpp +92 -0
  309. package/native/tests/test_image_cache.cpp +109 -0
  310. package/native/tests/test_js_runtime_access.cpp +137 -0
  311. package/native/tests/test_key_events.cpp +235 -0
  312. package/native/tests/test_momentum.cpp +138 -0
  313. package/native/tests/test_quit_file.cpp +104 -0
  314. package/native/tests/test_scroll_animation.cpp +120 -0
  315. package/native/tests/test_scroll_bounds.cpp +66 -0
  316. package/native/tests/test_scroll_indicator.cpp +149 -0
  317. package/native/tests/test_scroll_snap.cpp +112 -0
  318. package/native/tests/test_settle.cpp +115 -0
  319. package/native/tests/test_share.cpp +75 -0
  320. package/native/tests/test_sourcecode.cpp +62 -0
  321. package/native/tests/test_window_close.cpp +119 -0
  322. package/native/tests/test_window_limits.cpp +123 -0
  323. package/package.json +43 -0
  324. package/react-native.config.js +86 -0
  325. package/supported-versions.json +66 -0
@@ -0,0 +1,448 @@
1
+ #include "WindowsModule.h"
2
+
3
+ #include "KeyEvents.h"
4
+
5
+ #include "PlatformServices.h"
6
+ #include "DragAndDrop.h"
7
+ #include "WindowControl.h"
8
+ #include "WindowHost.h"
9
+
10
+ #include <react/bridging/Bridging.h>
11
+ #include <react/bridging/Promise.h>
12
+
13
+ #include <algorithm>
14
+ #include <string>
15
+ #include <vector>
16
+ #include <utility>
17
+
18
+ namespace basalt {
19
+
20
+ namespace {
21
+
22
+ using facebook::jsi::Array;
23
+ using facebook::jsi::Object;
24
+ using facebook::react::Tag;
25
+ using facebook::jsi::Runtime;
26
+ using facebook::jsi::String;
27
+ using facebook::jsi::Value;
28
+ using facebook::react::TurboModule;
29
+
30
+ std::string stringProperty(Runtime &runtime, const Object &options, const char *name) {
31
+ const Value value = options.getProperty(runtime, name);
32
+ return value.isString() ? value.asString(runtime).utf8(runtime) : std::string{};
33
+ }
34
+
35
+ double numberProperty(Runtime &runtime, const Object &options, const char *name, double fallback) {
36
+ const Value value = options.getProperty(runtime, name);
37
+ return value.isNumber() ? value.asNumber() : fallback;
38
+ }
39
+
40
+ } // namespace
41
+
42
+ namespace {
43
+
44
+ // Every live module, newest last.
45
+ //
46
+ // ReactCxxPlatform builds a *new* module for each lookup rather than caching
47
+ // one: kino's start-up makes six, and they come and go while the app runs. So
48
+ // "the module that installed the listener" is not a thing -- the one that
49
+ // installed it is routinely destroyed while others are still serving, and an
50
+ // unconditional clear in its destructor took the listeners with it. Keys were
51
+ // claimed, presses were matched, and `reportKey` then found no listener and
52
+ // dropped the answer. Nothing failed and nothing was logged; every declared
53
+ // shortcut in the app was simply dead.
54
+ //
55
+ // So the listeners belong to the *set*, not to an instance: installed when the
56
+ // first module appears, dispatched to whichever is live, and cleared only when
57
+ // the last one goes.
58
+ std::mutex &liveMutex() {
59
+ static std::mutex mutex;
60
+ return mutex;
61
+ }
62
+
63
+ std::vector<DesktopWindowsModule *> &liveModules() {
64
+ static std::vector<DesktopWindowsModule *> modules;
65
+ return modules;
66
+ }
67
+
68
+ } // namespace
69
+
70
+ DesktopWindowsModule::DesktopWindowsModule(std::shared_ptr<facebook::react::CallInvoker> jsInvoker)
71
+ : TurboModule(kModuleName, std::move(jsInvoker)) {
72
+ methodMap_["open"] = MethodMetadata{1, open};
73
+ methodMap_["close"] = MethodMetadata{1, close};
74
+ methodMap_["getWindows"] = MethodMetadata{0, getWindows};
75
+ methodMap_["interceptClose"] = MethodMetadata{2, interceptClose};
76
+ methodMap_["interceptQuit"] = MethodMetadata{1, interceptQuit};
77
+ methodMap_["quit"] = MethodMetadata{0, quit};
78
+ methodMap_["getDisplays"] = MethodMetadata{0, getDisplays};
79
+ methodMap_["getPointerPosition"] = MethodMetadata{0, getPointerPosition};
80
+ // Which keys a view handles. Two arguments rather than a props diff, because a
81
+ // <View> has nowhere in its C++ props for a list of combinations and a
82
+ // callback; see src/KeyHandler.tsx for why that rules out the nativeID idiom
83
+ // <DropTarget> uses.
84
+ methodMap_["setHandledKeys"] = MethodMetadata{2, setHandledKeys};
85
+ methodMap_["clearHandledKeys"] = MethodMetadata{1, clearHandledKeys};
86
+ // What a NativeEventEmitter over this module calls; the event goes out as a
87
+ // device event either way.
88
+ methodMap_["addListener"] = MethodMetadata{1, noop};
89
+ methodMap_["removeListeners"] = MethodMetadata{1, noop};
90
+
91
+ {
92
+ const std::lock_guard<std::mutex> lock(liveMutex());
93
+ liveModules().push_back(this);
94
+ }
95
+ installListeners();
96
+ }
97
+
98
+ /**
99
+ * Points every seam at this module.
100
+ *
101
+ * Called from the constructor, and again from another module's destructor when
102
+ * that one was the one the seams pointed at -- see liveModules(). Each of these
103
+ * captures `this`, which is safe only for as long as this module is live, and
104
+ * keeping that true is the whole job of the live set.
105
+ */
106
+ void DesktopWindowsModule::installListeners() {
107
+ // A window closed by the person rather than by the app. Without this the
108
+ // `<Window>` that opened it would go on believing it is open; see
109
+ // core/WindowHost.h.
110
+ setHostWindowClosedListener([this](facebook::react::SurfaceId surfaceId) {
111
+ emitDeviceEvent(kWindowClosedEvent,
112
+ [surfaceId](Runtime & /*runtime*/, std::vector<Value> &args) {
113
+ args.emplace_back(Value(static_cast<int>(surfaceId)));
114
+ });
115
+ });
116
+
117
+ // Somebody tried to close a window that asked to be asked first. The window
118
+ // is still open; what happens next is the app's decision.
119
+ setHostWindowCloseRequestListener([this](facebook::react::SurfaceId surfaceId) {
120
+ emitDeviceEvent(kWindowCloseRequestedEvent,
121
+ [surfaceId](Runtime & /*runtime*/, std::vector<Value> &args) {
122
+ args.emplace_back(Value(static_cast<int>(surfaceId)));
123
+ });
124
+ });
125
+
126
+ // And somebody trying to quit an application that asked to be asked. No
127
+ // argument: there is only one application.
128
+ // A monitor plugged in, unplugged or rearranged. No payload: an app that
129
+ // cares re-reads the list, which is the only way to be right when several
130
+ // changes arrive together.
131
+ setDisplaysListener([this]() {
132
+ emitDeviceEvent(kDisplaysChangedEvent,
133
+ [](Runtime & /*runtime*/, std::vector<Value> & /*args*/) {});
134
+ });
135
+
136
+ // Something dragged onto a view that said it would take one.
137
+
138
+ setKeyListener([this](Tag tag, const KeyCombination &pressed) {
139
+ emitDeviceEvent(kKeyEvent, [tag, pressed](Runtime &runtime, std::vector<Value> &args) {
140
+ Object payload(runtime);
141
+ payload.setProperty(runtime, "tag", Value(static_cast<int>(tag)));
142
+ payload.setProperty(runtime, "key",
143
+ String::createFromUtf8(runtime, pressed.key));
144
+ payload.setProperty(runtime, "altKey", Value(pressed.modifiers.alt));
145
+ payload.setProperty(runtime, "ctrlKey", Value(pressed.modifiers.ctrl));
146
+ payload.setProperty(runtime, "metaKey", Value(pressed.modifiers.meta));
147
+ payload.setProperty(runtime, "shiftKey", Value(pressed.modifiers.shift));
148
+ args.emplace_back(runtime, payload);
149
+ });
150
+ });
151
+ setDropListener([this](const DropEvent &drop) {
152
+ emitDeviceEvent(kDropEvent, [drop](Runtime &runtime, std::vector<Value> &args) {
153
+ Object payload(runtime);
154
+ payload.setProperty(runtime, "tag", Value(static_cast<int>(drop.tag)));
155
+ payload.setProperty(
156
+ runtime,
157
+ "phase",
158
+ String::createFromUtf8(runtime,
159
+ drop.phase == DropPhase::Over ? "over"
160
+ : drop.phase == DropPhase::Leave ? "leave"
161
+ : "drop"));
162
+ payload.setProperty(runtime, "x", Value(drop.x));
163
+ payload.setProperty(runtime, "y", Value(drop.y));
164
+
165
+ // Only a drop carries contents; see core/DragAndDrop.h for why an
166
+ // `over` deliberately does not.
167
+ Array files(runtime, drop.payload.files.size());
168
+ for (size_t i = 0; i < drop.payload.files.size(); i++) {
169
+ files.setValueAtIndex(
170
+ runtime, i, String::createFromUtf8(runtime, drop.payload.files[i]));
171
+ }
172
+ payload.setProperty(runtime, "files", files);
173
+ payload.setProperty(runtime, "text",
174
+ String::createFromUtf8(runtime, drop.payload.text));
175
+
176
+ args.emplace_back(std::move(payload));
177
+ });
178
+ });
179
+
180
+ setHostQuitRequestListener([this]() {
181
+ emitDeviceEvent(kQuitRequestedEvent,
182
+ [](Runtime & /*runtime*/, std::vector<Value> & /*args*/) {});
183
+ });
184
+ }
185
+
186
+
187
+ DesktopWindowsModule::~DesktopWindowsModule() {
188
+ DesktopWindowsModule *successor = nullptr;
189
+ {
190
+ const std::lock_guard<std::mutex> lock(liveMutex());
191
+ std::vector<DesktopWindowsModule *> &live = liveModules();
192
+ live.erase(std::remove(live.begin(), live.end(), this), live.end());
193
+ if (!live.empty()) {
194
+ // Newest, arbitrarily but consistently: any live one can emit, and the
195
+ // newest is the one a fresh lookup would have produced anyway.
196
+ successor = live.back();
197
+ }
198
+ }
199
+
200
+ if (successor != nullptr) {
201
+ // Handed over rather than cleared. The listeners capture a module, and this
202
+ // one is going; clearing them would silence a platform that still has five
203
+ // other modules serving, which is what happened to every keyboard shortcut
204
+ // in an app until this was found.
205
+ successor->installListeners();
206
+ return;
207
+ }
208
+
209
+ // The last one out. Now they can go.
210
+ setHostWindowClosedListener(nullptr);
211
+ setHostWindowCloseRequestListener(nullptr);
212
+ setHostQuitRequestListener(nullptr);
213
+ setDisplaysListener(nullptr);
214
+ setKeyListener(nullptr);
215
+ setDropListener(nullptr);
216
+ }
217
+
218
+ Value DesktopWindowsModule::noop(Runtime & /*runtime*/,
219
+ TurboModule & /*module*/,
220
+ const Value * /*args*/,
221
+ size_t /*count*/) {
222
+ return Value::undefined();
223
+ }
224
+
225
+ Value DesktopWindowsModule::open(Runtime &runtime,
226
+ TurboModule &module,
227
+ const Value *args,
228
+ size_t count) {
229
+ NewWindowOptions options;
230
+ if (count >= 1 && args[0].isObject()) {
231
+ const Object given = args[0].asObject(runtime);
232
+ options.component = stringProperty(runtime, given, "component");
233
+ options.title = stringProperty(runtime, given, "title");
234
+ options.width = numberProperty(runtime, given, "width", options.width);
235
+ options.height = numberProperty(runtime, given, "height", options.height);
236
+
237
+ const Value props = given.getProperty(runtime, "props");
238
+ if (props.isObject()) {
239
+ options.props = facebook::jsi::dynamicFromValue(runtime, props);
240
+ }
241
+ }
242
+
243
+ auto promise = std::make_shared<facebook::react::AsyncPromise<folly::dynamic>>(
244
+ runtime, static_cast<DesktopWindowsModule &>(module).jsInvoker_);
245
+
246
+ // Onto the UI thread, where a window may be made, and back through the
247
+ // promise. Blocking the JavaScript thread on a window manager is the thing
248
+ // this shape exists to avoid.
249
+ postToUiThread([promise, options = std::move(options)] {
250
+ const facebook::react::SurfaceId opened = openHostWindow(options);
251
+ // Null rather than zero for "could not": an id is a number, and a caller
252
+ // checking `if (id)` would read a failure as window zero.
253
+ promise->resolve(opened == 0 ? folly::dynamic(nullptr)
254
+ : folly::dynamic(static_cast<int>(opened)));
255
+ });
256
+
257
+ return Value(runtime,
258
+ facebook::react::bridging::toJs(
259
+ runtime, *promise, static_cast<DesktopWindowsModule &>(module).jsInvoker_));
260
+ }
261
+
262
+ Value DesktopWindowsModule::close(Runtime & /*runtime*/,
263
+ TurboModule & /*module*/,
264
+ const Value *args,
265
+ size_t count) {
266
+ if (count >= 1 && args[0].isNumber()) {
267
+ const auto surfaceId = static_cast<facebook::react::SurfaceId>(args[0].asNumber());
268
+ postToUiThread([surfaceId] { closeHostWindow(surfaceId); });
269
+ }
270
+ return Value::undefined();
271
+ }
272
+
273
+ Value DesktopWindowsModule::setHandledKeys(Runtime &runtime,
274
+ TurboModule & /*module*/,
275
+ const Value *args,
276
+ size_t count) {
277
+ if (count < 2 || !args[0].isNumber() || !args[1].isObject()) {
278
+ return Value::undefined();
279
+ }
280
+ const auto tag = static_cast<Tag>(args[0].asNumber());
281
+ Object list = args[1].getObject(runtime);
282
+ if (!list.isArray(runtime)) {
283
+ return Value::undefined();
284
+ }
285
+ Array combinations = list.getArray(runtime);
286
+ const size_t length = combinations.length(runtime);
287
+
288
+ std::vector<KeyCombination> claimed;
289
+ claimed.reserve(length);
290
+ for (size_t i = 0; i < length; i++) {
291
+ Value entry = combinations.getValueAtIndex(runtime, i);
292
+ if (!entry.isObject()) {
293
+ continue;
294
+ }
295
+ Object combination = entry.getObject(runtime);
296
+ Value key = combination.getProperty(runtime, "key");
297
+ if (!key.isString()) {
298
+ // A combination with no key is not one. Skipped rather than refused: an
299
+ // app rebuilding its list should not lose the rest of it to one bad entry.
300
+ continue;
301
+ }
302
+ const auto flag = [&](const char *name) {
303
+ Value value = combination.getProperty(runtime, name);
304
+ return value.isBool() && value.getBool();
305
+ };
306
+ claimed.push_back(KeyCombination{
307
+ key.getString(runtime).utf8(runtime),
308
+ KeyModifiers{flag("altKey"), flag("ctrlKey"), flag("metaKey"), flag("shiftKey")}});
309
+ }
310
+
311
+ // No hop, for the reason interceptClose gives: this is read on the UI thread
312
+ // inside a key handler that cannot wait for the JavaScript thread, and it is a
313
+ // vector behind a mutex rather than a toolkit call. Setting it late costs one
314
+ // keystroke that was not claimed.
315
+ basalt::setHandledKeys(tag, std::move(claimed));
316
+ return Value::undefined();
317
+ }
318
+
319
+ Value DesktopWindowsModule::clearHandledKeys(Runtime & /*runtime*/,
320
+ TurboModule & /*module*/,
321
+ const Value *args,
322
+ size_t count) {
323
+ if (count >= 1 && args[0].isNumber()) {
324
+ basalt::clearHandledKeys(static_cast<Tag>(args[0].asNumber()));
325
+ }
326
+ return Value::undefined();
327
+ }
328
+
329
+ Value DesktopWindowsModule::interceptClose(Runtime & /*runtime*/,
330
+ TurboModule & /*module*/,
331
+ const Value *args,
332
+ size_t count) {
333
+ if (count >= 1 && args[0].isNumber()) {
334
+ const auto surfaceId = static_cast<facebook::react::SurfaceId>(args[0].asNumber());
335
+ const bool intercepted = count >= 2 && args[1].isBool() && args[1].getBool();
336
+ // No hop. This is read from inside a close handler on the UI thread, which
337
+ // cannot wait for the JavaScript thread to get around to setting it -- and
338
+ // it is a flag behind a mutex, not a toolkit call, so there is nothing to
339
+ // marshal. Setting it late means one close that is not intercepted, which
340
+ // is a window that closes; setting it on a hop could mean a close handler
341
+ // reading a flag the app set several frames ago.
342
+ setHostWindowCloseIntercepted(surfaceId, intercepted);
343
+ }
344
+ return Value::undefined();
345
+ }
346
+
347
+ namespace {
348
+
349
+ // One display, as JavaScript sees it. Flat rather than nested rectangles:
350
+ // `bounds` and `workArea` as objects would read better and would mean two
351
+ // more allocations per display per call, and this is read while an app is
352
+ // deciding where to put a window.
353
+ Object displayObject(Runtime &runtime, const DisplayInfo &info) {
354
+ Object out(runtime);
355
+ out.setProperty(runtime, "x", Value(info.x));
356
+ out.setProperty(runtime, "y", Value(info.y));
357
+ out.setProperty(runtime, "width", Value(info.width));
358
+ out.setProperty(runtime, "height", Value(info.height));
359
+ out.setProperty(runtime, "workX", Value(info.workX));
360
+ out.setProperty(runtime, "workY", Value(info.workY));
361
+ out.setProperty(runtime, "workWidth", Value(info.workWidth));
362
+ out.setProperty(runtime, "workHeight", Value(info.workHeight));
363
+ out.setProperty(runtime, "scaleFactor", Value(info.scaleFactor));
364
+ out.setProperty(runtime, "primary", Value(info.primary));
365
+ return out;
366
+ }
367
+
368
+ } // namespace
369
+
370
+ Value DesktopWindowsModule::getDisplays(Runtime &runtime,
371
+ TurboModule & /*module*/,
372
+ const Value * /*args*/,
373
+ size_t /*count*/) {
374
+ // Off the cache rather than the toolkit, so this needs no hop and can be
375
+ // read during render -- the same arrangement getBounds has. The host
376
+ // refreshes it at startup and on every change, so it is exact.
377
+ const std::vector<DisplayInfo> found = lastKnownDisplays();
378
+
379
+ Array result(runtime, found.size());
380
+ for (size_t i = 0; i < found.size(); i++) {
381
+ result.setValueAtIndex(runtime, i, displayObject(runtime, found[i]));
382
+ }
383
+ return result;
384
+ }
385
+
386
+ Value DesktopWindowsModule::getPointerPosition(Runtime &runtime,
387
+ TurboModule &module,
388
+ const Value * /*args*/,
389
+ size_t /*count*/) {
390
+ auto promise = std::make_shared<facebook::react::AsyncPromise<folly::dynamic>>(
391
+ runtime, static_cast<DesktopWindowsModule &>(module).jsInvoker_);
392
+
393
+ // A promise rather than a cached read, because there is nothing to cache:
394
+ // the answer changes whenever the pointer moves and no host reports that.
395
+ // So this is the one display question that has to go and ask.
396
+ postToUiThread([promise] {
397
+ const PointerPosition where = pointerPosition();
398
+ folly::dynamic out = folly::dynamic::object;
399
+ out["x"] = where.x;
400
+ out["y"] = where.y;
401
+ // So that an app can tell "at the origin" from "this desktop will not
402
+ // say" -- which GTK never will; see GtkWindowControl.cpp.
403
+ out["known"] = where.known;
404
+ promise->resolve(std::move(out));
405
+ });
406
+
407
+ return Value(runtime,
408
+ facebook::react::bridging::toJs(
409
+ runtime, *promise, static_cast<DesktopWindowsModule &>(module).jsInvoker_));
410
+ }
411
+
412
+ Value DesktopWindowsModule::quit(Runtime & /*runtime*/,
413
+ TurboModule & /*module*/,
414
+ const Value * /*args*/,
415
+ size_t /*count*/) {
416
+ // Hopped, unlike interceptQuit: this one ends the process through a
417
+ // toolkit call, and those belong on the thread that owns the toolkit.
418
+ postToUiThread([] { quitHost(); });
419
+ return Value::undefined();
420
+ }
421
+
422
+ Value DesktopWindowsModule::interceptQuit(Runtime & /*runtime*/,
423
+ TurboModule & /*module*/,
424
+ const Value *args,
425
+ size_t count) {
426
+ // No hop, for the reason interceptClose gives: this is read on the UI
427
+ // thread from inside a terminate handler that cannot wait for the
428
+ // JavaScript thread, and it is a flag behind a mutex rather than a toolkit
429
+ // call.
430
+ setHostQuitIntercepted(count >= 1 && args[0].isBool() && args[0].getBool());
431
+ return Value::undefined();
432
+ }
433
+
434
+ Value DesktopWindowsModule::getWindows(Runtime &runtime,
435
+ TurboModule & /*module*/,
436
+ const Value * /*args*/,
437
+ size_t /*count*/) {
438
+ // Read rather than hopped: this is bookkeeping the host keeps in a vector,
439
+ // and an app asking during render cannot wait for a thread.
440
+ const std::vector<facebook::react::SurfaceId> open = hostWindows();
441
+ Array result(runtime, open.size());
442
+ for (size_t i = 0; i < open.size(); i++) {
443
+ result.setValueAtIndex(runtime, i, Value(static_cast<int>(open[i])));
444
+ }
445
+ return Value(runtime, result);
446
+ }
447
+
448
+ } // namespace basalt
@@ -0,0 +1,133 @@
1
+ // `BasaltWindows`: opening and closing windows, from JavaScript.
2
+ //
3
+ // Three methods over core/WindowHost.h. What is worth saying is what a window
4
+ // *is* here, because it decides the shape of everything above: one window is
5
+ // one surface is one React root. See that header.
6
+ //
7
+ // Separate from `BasaltWindow`, which is the singular one next door: that
8
+ // module is about the window an app is already in -- its size, its title, its
9
+ // caption buttons -- and this one is about there being more than one of them.
10
+ // Merging them would mean one module whose methods mean different things
11
+ // depending on which half you are in.
12
+
13
+ #pragma once
14
+
15
+ #include <ReactCommon/TurboModule.h>
16
+
17
+ #include <memory>
18
+
19
+ namespace basalt {
20
+
21
+ class DesktopWindowsModule : public facebook::react::TurboModule {
22
+ public:
23
+ static constexpr const char *kModuleName = "BasaltWindows";
24
+
25
+ explicit DesktopWindowsModule(std::shared_ptr<facebook::react::CallInvoker> jsInvoker);
26
+
27
+ // Points every seam at this module. Called by the constructor, and by another
28
+ // module's destructor when it hands over; see the .cpp's liveModules().
29
+ void installListeners();
30
+ ~DesktopWindowsModule() override;
31
+
32
+ private:
33
+ // `open({component, title, width, height, props})` -> a promise of the new
34
+ // window's id, or of null when the platform could not open one.
35
+ //
36
+ // A promise rather than a number, because the window is made on the UI
37
+ // thread: this is called from the JavaScript one, and a synchronous answer
38
+ // would mean blocking it on a window manager.
39
+ static facebook::jsi::Value open(facebook::jsi::Runtime &runtime,
40
+ facebook::react::TurboModule &module,
41
+ const facebook::jsi::Value *args,
42
+ size_t count);
43
+ static facebook::jsi::Value close(facebook::jsi::Runtime &runtime,
44
+ facebook::react::TurboModule &module,
45
+ const facebook::jsi::Value *args,
46
+ size_t count);
47
+ // Every window that is open, main one first. For an app asking what it has,
48
+ // and for a test asserting that a window really closed.
49
+ static facebook::jsi::Value getWindows(facebook::jsi::Runtime &runtime,
50
+ facebook::react::TurboModule &module,
51
+ const facebook::jsi::Value *args,
52
+ size_t count);
53
+ // `interceptClose(surfaceId, true)` -- this window refuses the window manager
54
+ // and reports the attempt instead. See core/WindowHost.h for why it is a flag
55
+ // set in advance rather than an answer given when asked.
56
+ static facebook::jsi::Value interceptClose(facebook::jsi::Runtime &runtime,
57
+ facebook::react::TurboModule &module,
58
+ const facebook::jsi::Value *args,
59
+ size_t count);
60
+ static facebook::jsi::Value setHandledKeys(facebook::jsi::Runtime &runtime,
61
+ facebook::react::TurboModule &module,
62
+ const facebook::jsi::Value *args,
63
+ size_t count);
64
+ static facebook::jsi::Value clearHandledKeys(facebook::jsi::Runtime &runtime,
65
+ facebook::react::TurboModule &module,
66
+ const facebook::jsi::Value *args,
67
+ size_t count);
68
+
69
+ // `getDisplays()` -- every display, primary first. Synchronous, off the
70
+ // cache the host keeps up to date; see core/WindowControl.h.
71
+ static facebook::jsi::Value getDisplays(facebook::jsi::Runtime &runtime,
72
+ facebook::react::TurboModule &module,
73
+ const facebook::jsi::Value *args,
74
+ size_t count);
75
+
76
+ // `getPointerPosition()` -- a promise, unlike the above: there is nothing
77
+ // to cache, because the answer changes whenever the pointer moves and
78
+ // nothing reports that.
79
+ static facebook::jsi::Value getPointerPosition(facebook::jsi::Runtime &runtime,
80
+ facebook::react::TurboModule &module,
81
+ const facebook::jsi::Value *args,
82
+ size_t count);
83
+
84
+ // `quit()` -- end the application. What a quit handler calls when it is
85
+ // ready, since intercepting refused the quit the system asked for.
86
+ static facebook::jsi::Value quit(facebook::jsi::Runtime &runtime,
87
+ facebook::react::TurboModule &module,
88
+ const facebook::jsi::Value *args,
89
+ size_t count);
90
+
91
+ // `interceptQuit(true)` -- the same for the application. One argument
92
+ // rather than two, because quitting is not about a particular window.
93
+ static facebook::jsi::Value interceptQuit(facebook::jsi::Runtime &runtime,
94
+ facebook::react::TurboModule &module,
95
+ const facebook::jsi::Value *args,
96
+ size_t count);
97
+ static facebook::jsi::Value noop(facebook::jsi::Runtime &runtime,
98
+ facebook::react::TurboModule &module,
99
+ const facebook::jsi::Value *args,
100
+ size_t count);
101
+ };
102
+
103
+ // The device event a window's closing arrives on. Named here so the two sides
104
+ // cannot drift.
105
+ inline constexpr const char *kWindowClosedEvent = "basaltWindowClosed";
106
+
107
+ // And the one a window's *attempted* closing arrives on: somebody tried, the
108
+ // window is still open, and the app decides. Only ever sent for a window that
109
+ // asked to intercept.
110
+ inline constexpr const char *kWindowCloseRequestedEvent = "basaltWindowCloseRequested";
111
+
112
+ // The application, not a window: somebody tried to quit and the app asked to
113
+ // be asked first. Carries no argument, because there is nothing to
114
+ // distinguish -- see core/WindowHost.h.
115
+ inline constexpr const char *kQuitRequestedEvent = "basaltQuitRequested";
116
+
117
+ // A monitor plugged in, unplugged, or rearranged. No argument: an app that
118
+ // cares re-reads the list, which is the only way to be right when several
119
+ // changes arrive at once.
120
+ inline constexpr const char *kDisplaysChangedEvent = "basaltDisplaysChanged";
121
+
122
+ // Something dragged over, off, or onto a view that marked itself a drop
123
+ // target. Carries the tag, the phase, the position and -- on a drop only --
124
+ // what was dropped. See core/DragAndDrop.h.
125
+ inline constexpr const char *kDropEvent = "basaltDrop";
126
+
127
+ // A key combination a view declared through `<KeyHandler>`. Carries the tag and
128
+ // the combination, and arrives only for combinations some view claimed -- the
129
+ // host has already decided that, synchronously, because it had to consume the key
130
+ // or pass it on before this could be sent. See core/KeyEvents.h.
131
+ inline constexpr const char *kKeyEvent = "basaltKey";
132
+
133
+ } // namespace basalt