basalt-win32 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/native/CMakeLists.txt +56 -9
- package/native/tests/test_win32_input.cpp +82 -0
- package/native/tests/test_win32_peer_clip.cpp +266 -0
- package/native/tests/test_win32_run_loop.cpp +122 -0
- package/native/tests/test_win32_textinput.cpp +108 -0
- package/native/tests/test_win32_ui_timer.cpp +195 -0
- package/native/win32/PlatformServicesWin32.cpp +16 -0
- package/native/win32/RnWin32View.h +7 -0
- package/native/win32/Win32AnimationChoreographer.cpp +15 -32
- package/native/win32/Win32AnimationChoreographer.h +9 -6
- package/native/win32/Win32Cursors.cpp +13 -17
- package/native/win32/Win32MountingManager.cpp +6 -3
- package/native/win32/Win32PeerClip.cpp +153 -0
- package/native/win32/Win32PeerClip.h +92 -0
- package/native/win32/Win32RunLoopObserver.cpp +39 -9
- package/native/win32/Win32RunLoopObserver.h +41 -2
- package/native/win32/Win32ScrollView.cpp +13 -32
- package/native/win32/Win32ScrollView.h +5 -6
- package/native/win32/Win32SkiaPeer.h +26 -4
- package/native/win32/Win32TextInput.cpp +106 -46
- package/native/win32/Win32TextInput.h +23 -8
- package/native/win32/Win32UiThread.h +11 -0
- package/native/win32/Win32UiTimer.cpp +139 -0
- package/native/win32/Win32UiTimer.h +65 -0
- package/native/win32/main_win32.cpp +101 -91
- package/package.json +2 -2
|
@@ -55,6 +55,14 @@
|
|
|
55
55
|
#ifdef BASALT_HAS_SKIA
|
|
56
56
|
#include "Win32SkiaModule.h"
|
|
57
57
|
#endif
|
|
58
|
+
#if BASALT_HAS_SKIA_GRAPHITE
|
|
59
|
+
// The 3.x module, from basalt-skia. Registered here rather than through
|
|
60
|
+
// BASALT_PACKAGE_TURBO_MODULES because that property has core generate the
|
|
61
|
+
// call, and core is shared with hosts that cannot link this Skia -- see that
|
|
62
|
+
// package's CMakeLists. The same arrangement the 2.x module above has, and
|
|
63
|
+
// the same one main_appkit.mm makes.
|
|
64
|
+
#include "BasaltSkiaModule.h"
|
|
65
|
+
#endif
|
|
58
66
|
#include "Win32Packaging.h"
|
|
59
67
|
#include "PointerButtons.h"
|
|
60
68
|
#include "WindowControl.h"
|
|
@@ -70,6 +78,7 @@
|
|
|
70
78
|
#include "TextSelection.h"
|
|
71
79
|
#include "Win32TouchDispatcher.h"
|
|
72
80
|
#include "Win32UiThread.h"
|
|
81
|
+
#include "Win32UiTimer.h"
|
|
73
82
|
#include "DragAndDrop.h"
|
|
74
83
|
#include "Win32DropTarget.h"
|
|
75
84
|
#include "Win32TitleBar.h"
|
|
@@ -162,16 +171,6 @@ constexpr int kInitialHeight = 700;
|
|
|
162
171
|
// See e2e/demo.js. Only used when no module name is given.
|
|
163
172
|
constexpr const char *kRenderFunctionName = "basaltRender";
|
|
164
173
|
|
|
165
|
-
constexpr UINT_PTR kSecondTreeTimer = 100;
|
|
166
|
-
|
|
167
|
-
// BASALT_QUIT_AFTER_MS's timer. A constant rather than the literal 101 it used
|
|
168
|
-
// to be written as at its one call site: the spinner timer below was given 101
|
|
169
|
-
// too, and `SetTimer` with an id that is already in use *replaces* the timer
|
|
170
|
-
// rather than failing. An app with an <ActivityIndicator> in it therefore
|
|
171
|
-
// silently cancelled its own shutdown, and the only symptom was a host that
|
|
172
|
-
// never exited.
|
|
173
|
-
constexpr UINT_PTR kQuitAfterTimer = 101;
|
|
174
|
-
|
|
175
174
|
// One window, and the surface in it.
|
|
176
175
|
//
|
|
177
176
|
// A window is a surface: a surface is what has a size, a layout context and a
|
|
@@ -393,12 +392,16 @@ void requestRepaint() {
|
|
|
393
392
|
// Win32AnimationChoreographer.h gives about its own timer: a timer that runs
|
|
394
393
|
// whether or not anything is animating wakes the process sixty times a second
|
|
395
394
|
// forever, which matters more on a laptop than the frame interval does.
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
//
|
|
399
|
-
//
|
|
400
|
-
|
|
401
|
-
|
|
395
|
+
//
|
|
396
|
+
// Not `SetTimer`, and this is the timer that proves why: the one thing it
|
|
397
|
+
// exists to do is repaint, and a `WM_TIMER` is the message Windows withholds
|
|
398
|
+
// while there is painting to do. A spinner that stops the moment the window
|
|
399
|
+
// gets busy is the exact failure. See win32/Win32UiTimer.h.
|
|
400
|
+
basalt::UiTimerId gAnimationTimer = 0;
|
|
401
|
+
|
|
402
|
+
// Defined below, and named here because the timer that calls it is started
|
|
403
|
+
// above it.
|
|
404
|
+
void tickAnimations(HWND hwnd);
|
|
402
405
|
// When the last tick ran, so a frame delay is measured rather than assumed.
|
|
403
406
|
// A GIF's delays are in hundredths of a second and the timer is 16ms, so
|
|
404
407
|
// counting ticks would quantise every delay to a multiple of the tick and a
|
|
@@ -413,15 +416,16 @@ void updateAnimationTimer() {
|
|
|
413
416
|
gHost.main().root->hasAnimatedImage() ||
|
|
414
417
|
(gHost.logBoxRoot != nullptr &&
|
|
415
418
|
(gHost.logBoxRoot->hasAnimatingSpinner() || gHost.logBoxRoot->hasAnimatedImage()));
|
|
416
|
-
if (wanted ==
|
|
419
|
+
if (wanted == (gAnimationTimer != 0)) {
|
|
417
420
|
return;
|
|
418
421
|
}
|
|
419
|
-
gAnimationTimerRunning = wanted;
|
|
420
422
|
if (wanted) {
|
|
421
423
|
gLastAnimationTick = std::chrono::steady_clock::now();
|
|
422
|
-
|
|
424
|
+
HWND window = gHost.main().window;
|
|
425
|
+
gAnimationTimer = basalt::scheduleUiTimer(16, true, [window] { tickAnimations(window); });
|
|
423
426
|
} else {
|
|
424
|
-
|
|
427
|
+
basalt::cancelUiTimer(gAnimationTimer);
|
|
428
|
+
gAnimationTimer = 0;
|
|
425
429
|
}
|
|
426
430
|
}
|
|
427
431
|
|
|
@@ -566,6 +570,22 @@ facebook::react::TurboModuleProviders makeTurboModuleProviders(
|
|
|
566
570
|
if (name == basalt::Win32SkiaModule::kModuleName) {
|
|
567
571
|
return std::make_shared<basalt::Win32SkiaModule>(jsInvoker);
|
|
568
572
|
}
|
|
573
|
+
#endif
|
|
574
|
+
#if BASALT_HAS_SKIA_GRAPHITE
|
|
575
|
+
// And 3.x, which is Graphite over Dawn and a different module under
|
|
576
|
+
// the same name. Never both: cmake/Skia.cmake refuses a 3.x install by
|
|
577
|
+
// version, so BASALT_HAS_SKIA is defined in exactly the builds this is
|
|
578
|
+
// not.
|
|
579
|
+
//
|
|
580
|
+
// Without this the canvas is built, linked and unreachable: the view
|
|
581
|
+
// mounts, basalt-skia's C++ is all in the binary, and the app still
|
|
582
|
+
// ends at getEnforcing('RNSkiaModule') on its first Skia import --
|
|
583
|
+
// which is where kino's first Windows run stopped.
|
|
584
|
+
if (name == basalt::BasaltSkiaModule::kModuleName) {
|
|
585
|
+
if (auto module = basalt::makeSkiaTurboModule(name, jsInvoker)) {
|
|
586
|
+
return module;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
569
589
|
#endif
|
|
570
590
|
if (name == basalt::DesktopBlobModule::kModuleName) {
|
|
571
591
|
return std::make_shared<basalt::DesktopBlobModule>(jsInvoker);
|
|
@@ -898,25 +918,18 @@ struct ScriptedInput {
|
|
|
898
918
|
// Fired from timers keyed by index, so the vector has to outlive the loop.
|
|
899
919
|
std::vector<ScriptedInput> gScriptedInput;
|
|
900
920
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
// Every timer id this host uses is distinct.
|
|
921
|
+
// There is no table of timer ids here any more, and that is the point.
|
|
904
922
|
//
|
|
905
|
-
//
|
|
906
|
-
//
|
|
907
|
-
//
|
|
908
|
-
//
|
|
909
|
-
//
|
|
910
|
-
//
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
static_assert(kScriptedInputTimerBase > kSecondTreeTimer &&
|
|
916
|
-
kScriptedInputTimerBase > kQuitAfterTimer &&
|
|
917
|
-
kScriptedInputTimerBase > kAnimationTimer &&
|
|
918
|
-
kScriptedInputTimerBase > kQuitFileTimer,
|
|
919
|
-
"the scripted-input ids count up from the base and must start above the rest");
|
|
923
|
+
// Every one of this host's waits used to be a `SetTimer` id written by hand,
|
|
924
|
+
// and `SetTimer` with an id already in use *replaces* the timer rather than
|
|
925
|
+
// failing -- so a collision was silent and showed up as whatever the replaced
|
|
926
|
+
// timer had been for. It happened: the spinner's repaint timer was given the
|
|
927
|
+
// same id as BASALT_QUIT_AFTER_MS's, and an app with an <ActivityIndicator>
|
|
928
|
+
// in it cancelled its own shutdown and hung. Two static_asserts guarded
|
|
929
|
+
// against the next one.
|
|
930
|
+
//
|
|
931
|
+
// win32/Win32UiTimer.h hands out its own ids from a counter, so there is
|
|
932
|
+
// nothing left to collide and nothing left to assert about.
|
|
920
933
|
|
|
921
934
|
// The numbers in "10,20,30" -- however many there are, which is what lets one
|
|
922
935
|
// parser read both a tap's pair and a drag's four.
|
|
@@ -938,9 +951,10 @@ std::vector<double> parseNumbers(const std::string &spec) {
|
|
|
938
951
|
return numbers;
|
|
939
952
|
}
|
|
940
953
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
954
|
+
// One scheduled action, on the UI thread. An index and a window rather than a
|
|
955
|
+
// TIMERPROC's four arguments: the timer behind this carries a closure, so
|
|
956
|
+
// there is nothing to recover from an id.
|
|
957
|
+
void fireScriptedInput(HWND hwnd, size_t index) {
|
|
944
958
|
if (index >= gScriptedInput.size()) {
|
|
945
959
|
return;
|
|
946
960
|
}
|
|
@@ -1159,10 +1173,13 @@ void CALLBACK fireScriptedInput(HWND hwnd, UINT, UINT_PTR id, DWORD) {
|
|
|
1159
1173
|
// the last one, so a drag can be scheduled behind the taps.
|
|
1160
1174
|
UINT scheduleScriptedInput(const ScriptedInput &action, UINT delayMs) {
|
|
1161
1175
|
gScriptedInput.push_back(action);
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1176
|
+
const size_t index = gScriptedInput.size() - 1;
|
|
1177
|
+
HWND window = gHost.main().window;
|
|
1178
|
+
// Not `SetTimer`: scripted input exists to drive a *running* app, and the
|
|
1179
|
+
// apps worth driving are the ones that paint -- which is exactly when a
|
|
1180
|
+
// WM_TIMER never arrives. Every instrument in this file was silently
|
|
1181
|
+
// inoperative on an app with a `<Canvas>`. See win32/Win32UiTimer.h.
|
|
1182
|
+
basalt::scheduleUiTimer(delayMs, false, [window, index] { fireScriptedInput(window, index); });
|
|
1166
1183
|
return delayMs + 1000;
|
|
1167
1184
|
}
|
|
1168
1185
|
|
|
@@ -1804,17 +1821,9 @@ LRESULT CALLBACK hostProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
|
|
|
1804
1821
|
}
|
|
1805
1822
|
return 0;
|
|
1806
1823
|
|
|
1807
|
-
case
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
return 0;
|
|
1811
|
-
}
|
|
1812
|
-
if (wparam == kSecondTreeTimer) {
|
|
1813
|
-
KillTimer(hwnd, kSecondTreeTimer);
|
|
1814
|
-
std::fprintf(stderr, "--- committing tree 2 from JS ---\n");
|
|
1815
|
-
callRenderFunction(2);
|
|
1816
|
-
}
|
|
1817
|
-
return 0;
|
|
1824
|
+
// No WM_TIMER case. Nothing this host schedules arrives that way any
|
|
1825
|
+
// more -- see win32/Win32UiTimer.h for why it could not be relied on --
|
|
1826
|
+
// and swallowing the message would hide a timer somebody else set.
|
|
1818
1827
|
|
|
1819
1828
|
case WM_PAINT: {
|
|
1820
1829
|
PAINTSTRUCT paint{};
|
|
@@ -2529,7 +2538,10 @@ int main(int argc, char **argv) {
|
|
|
2529
2538
|
if (gHost.moduleName.empty()) {
|
|
2530
2539
|
std::fprintf(stderr, "--- committing tree 1 from JS ---\n");
|
|
2531
2540
|
callRenderFunction(1);
|
|
2532
|
-
|
|
2541
|
+
basalt::scheduleUiTimer(2000, false, [] {
|
|
2542
|
+
std::fprintf(stderr, "--- committing tree 2 from JS ---\n");
|
|
2543
|
+
callRenderFunction(2);
|
|
2544
|
+
});
|
|
2533
2545
|
}
|
|
2534
2546
|
|
|
2535
2547
|
UINT scriptedDelayMs = 1500;
|
|
@@ -2643,21 +2655,20 @@ int main(int argc, char **argv) {
|
|
|
2643
2655
|
// clicking anything. The same escape hatch both other hosts have.
|
|
2644
2656
|
if (const char *quitAfter = std::getenv("BASALT_QUIT_AFTER_MS")) {
|
|
2645
2657
|
const UINT delay = static_cast<UINT>(std::strtoul(quitAfter, nullptr, 10));
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
});
|
|
2658
|
+
HWND window = gHost.main().window;
|
|
2659
|
+
basalt::scheduleUiTimer(delay, false, [window] {
|
|
2660
|
+
// The same line the other two hosts log, so a person reading three
|
|
2661
|
+
// transcripts side by side sees the same shutdown in each. This
|
|
2662
|
+
// host was silent about it, which is how a scenario came to assert
|
|
2663
|
+
// on a string only two of them print.
|
|
2664
|
+
std::fprintf(stderr, "BASALT_QUIT_AFTER_MS elapsed; quitting\n");
|
|
2665
|
+
// What WM_CLOSE does, without going through WM_CLOSE. This is the
|
|
2666
|
+
// harness ending the run, not a person closing a window, and an app
|
|
2667
|
+
// that intercepts its own close would otherwise refuse it -- a test
|
|
2668
|
+
// that asks "does refusing work" by never exiting.
|
|
2669
|
+
captureBeforeTeardown();
|
|
2670
|
+
DestroyWindow(window);
|
|
2671
|
+
});
|
|
2661
2672
|
}
|
|
2662
2673
|
|
|
2663
2674
|
// BASALT_TEST_QUIT_FILE: the same shutdown, at a moment the harness picks
|
|
@@ -2671,26 +2682,25 @@ int main(int argc, char **argv) {
|
|
|
2671
2682
|
//
|
|
2672
2683
|
// Two instruments, one timer, because they want the same thing at different
|
|
2673
2684
|
// moments: the quit file is the harness deciding, BASALT_QUIT_WHEN_SETTLED is
|
|
2674
|
-
// the app noticing it has nothing left to do.
|
|
2675
|
-
// SetTimer replaces silently on a collision. See core/TestSettle.h.
|
|
2685
|
+
// the app noticing it has nothing left to do. See core/TestSettle.h.
|
|
2676
2686
|
if (basalt::testQuitFilePath().has_value() || basalt::quitWhenSettledMs().has_value()) {
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2687
|
+
HWND window = gHost.main().window;
|
|
2688
|
+
static basalt::UiTimerId poll = 0;
|
|
2689
|
+
poll = basalt::scheduleUiTimer(basalt::kTestQuitFilePollMs, true, [window] {
|
|
2690
|
+
const char *why = nullptr;
|
|
2691
|
+
if (basalt::testQuitFileAppeared()) {
|
|
2692
|
+
why = basalt::kTestQuitFileMessage;
|
|
2693
|
+
} else if (basalt::hasSettled()) {
|
|
2694
|
+
why = basalt::kQuitWhenSettledMessage;
|
|
2695
|
+
} else {
|
|
2696
|
+
return;
|
|
2697
|
+
}
|
|
2698
|
+
basalt::cancelUiTimer(poll);
|
|
2699
|
+
poll = 0;
|
|
2700
|
+
std::fprintf(stderr, "%s\n", why);
|
|
2701
|
+
captureBeforeTeardown();
|
|
2702
|
+
DestroyWindow(window);
|
|
2703
|
+
});
|
|
2694
2704
|
}
|
|
2695
2705
|
|
|
2696
2706
|
// Not a plain GetMessage loop: the event beat has to be induced each time the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "basalt-win32",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "The Windows view layer for basalt-core: a Win32 and Direct2D view layer, a Fabric mounting manager, and a host. Everything behind them is in basalt-core.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -22,6 +22,6 @@
|
|
|
22
22
|
"react-native": "*"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"basalt-core": "0.2.
|
|
25
|
+
"basalt-core": "0.2.2"
|
|
26
26
|
}
|
|
27
27
|
}
|