@fressh/react-native-terminal 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +182 -0
- package/ReactNativeTerminal.podspec +82 -0
- package/android/CMakeLists.txt +76 -0
- package/android/build.gradle +121 -0
- package/android/fix-prefab.gradle +51 -0
- package/android/src/main/assets/fonts/DejaVuSansMono.ttf +0 -0
- package/android/src/main/cpp/cpp-adapter.cpp +180 -0
- package/android/src/main/java/com/margelo/nitro/fressh/HybridTerminal.kt +146 -0
- package/android/src/main/java/com/margelo/nitro/fressh/ReactNativeTerminalModule.kt +43 -0
- package/android/src/main/java/com/margelo/nitro/fressh/ReactNativeTerminalPackage.kt +32 -0
- package/android/src/main/jniLibs/arm64-v8a/libshim_uniffi.so +0 -0
- package/android/src/main/jniLibs/x86_64/libshim_uniffi.so +0 -0
- package/babel.config.js +12 -0
- package/cpp/README.md +14 -0
- package/cpp/generated/shim_uniffi.cpp +4246 -0
- package/cpp/generated/shim_uniffi.hpp +364 -0
- package/ios/FresshTerminalRenderABI.h +46 -0
- package/ios/HybridTerminal.swift +144 -0
- package/ios/ReactNativeTerminalUniffi.mm +77 -0
- package/ios/fetch-angle.sh +39 -0
- package/ios/fonts/DejaVuSansMono.ttf +0 -0
- package/libEGL.xcframework/Info.plist +44 -0
- package/libEGL.xcframework/ios-arm64/libEGL.framework/Info.plist +0 -0
- package/libEGL.xcframework/ios-arm64/libEGL.framework/libEGL +0 -0
- package/libEGL.xcframework/ios-arm64_x86_64-simulator/libEGL.framework/Info.plist +0 -0
- package/libEGL.xcframework/ios-arm64_x86_64-simulator/libEGL.framework/libEGL +0 -0
- package/libGLESv2.xcframework/Info.plist +44 -0
- package/libGLESv2.xcframework/ios-arm64/libGLESv2.framework/Info.plist +0 -0
- package/libGLESv2.xcframework/ios-arm64/libGLESv2.framework/libGLESv2 +0 -0
- package/libGLESv2.xcframework/ios-arm64_x86_64-simulator/libGLESv2.framework/Info.plist +0 -0
- package/libGLESv2.xcframework/ios-arm64_x86_64-simulator/libGLESv2.framework/libGLESv2 +0 -0
- package/nitro/Terminal.nitro.ts +40 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/ReactNativeTerminal+autolinking.cmake +83 -0
- package/nitrogen/generated/android/ReactNativeTerminal+autolinking.gradle +27 -0
- package/nitrogen/generated/android/ReactNativeTerminalOnLoad.cpp +56 -0
- package/nitrogen/generated/android/ReactNativeTerminalOnLoad.hpp +34 -0
- package/nitrogen/generated/android/c++/JHybridTerminalSpec.cpp +76 -0
- package/nitrogen/generated/android/c++/JHybridTerminalSpec.hpp +68 -0
- package/nitrogen/generated/android/c++/views/JHybridTerminalStateUpdater.cpp +64 -0
- package/nitrogen/generated/android/c++/views/JHybridTerminalStateUpdater.hpp +49 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/fressh/HybridTerminalSpec.kt +69 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/fressh/ReactNativeTerminalOnLoad.kt +35 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/fressh/views/HybridTerminalManager.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/fressh/views/HybridTerminalStateUpdater.kt +23 -0
- package/nitrogen/generated/ios/ReactNativeTerminal+autolinking.rb +62 -0
- package/nitrogen/generated/ios/ReactNativeTerminal-Swift-Cxx-Bridge.cpp +33 -0
- package/nitrogen/generated/ios/ReactNativeTerminal-Swift-Cxx-Bridge.hpp +57 -0
- package/nitrogen/generated/ios/ReactNativeTerminal-Swift-Cxx-Umbrella.hpp +43 -0
- package/nitrogen/generated/ios/ReactNativeTerminalAutolinking.mm +33 -0
- package/nitrogen/generated/ios/ReactNativeTerminalAutolinking.swift +26 -0
- package/nitrogen/generated/ios/c++/HybridTerminalSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridTerminalSpecSwift.hpp +96 -0
- package/nitrogen/generated/ios/c++/views/HybridTerminalComponent.mm +132 -0
- package/nitrogen/generated/ios/swift/HybridTerminalSpec.swift +57 -0
- package/nitrogen/generated/ios/swift/HybridTerminalSpec_cxx.swift +204 -0
- package/nitrogen/generated/shared/c++/HybridTerminalSpec.cpp +26 -0
- package/nitrogen/generated/shared/c++/HybridTerminalSpec.hpp +68 -0
- package/nitrogen/generated/shared/c++/views/HybridTerminalComponent.cpp +110 -0
- package/nitrogen/generated/shared/c++/views/HybridTerminalComponent.hpp +113 -0
- package/nitrogen/generated/shared/json/TerminalConfig.json +12 -0
- package/package.json +97 -0
- package/react-native.config.js +12 -0
- package/shim_uniffi.xcframework/Info.plist +44 -0
- package/shim_uniffi.xcframework/ios-arm64/libshim_uniffi.a +0 -0
- package/shim_uniffi.xcframework/ios-arm64_x86_64-simulator/libshim_uniffi.a +0 -0
- package/src/Terminal.tsx +135 -0
- package/src/generated/shim_uniffi-ffi.ts +320 -0
- package/src/generated/shim_uniffi.ts +2527 -0
- package/src/index.ts +52 -0
- package/src/ssh.ts +239 -0
- package/tsconfig.json +31 -0
- package/ubrn.config.yaml +24 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// JNI bridge for the single package .so:
|
|
2
|
+
// 1. Render plane: HybridTerminal.kt <-> the Rust render C-ABI (attach/draw/
|
|
3
|
+
// resize/send_input/destroy) in libshim_uniffi.so.
|
|
4
|
+
// 2. Control plane install: ReactNativeTerminalModule.nativeInstallRustCrate ->
|
|
5
|
+
// NativeShimUniffi::registerModule (installs globalThis.NativeShimUniffi so the
|
|
6
|
+
// ubrn-generated TS bindings can call the uniffi scaffolding). See docs §8/§10.
|
|
7
|
+
|
|
8
|
+
#include <android/native_window_jni.h>
|
|
9
|
+
#include <cstddef>
|
|
10
|
+
#include <cstdint>
|
|
11
|
+
#include <jni.h>
|
|
12
|
+
|
|
13
|
+
#include <ReactCommon/CallInvoker.h>
|
|
14
|
+
#include <ReactCommon/CallInvokerHolder.h>
|
|
15
|
+
#include <fbjni/fbjni.h>
|
|
16
|
+
#include <jsi/jsi.h>
|
|
17
|
+
|
|
18
|
+
#include "shim_uniffi.hpp" // generated: NativeShimUniffi::registerModule
|
|
19
|
+
#include "ReactNativeTerminalOnLoad.hpp" // generated: registerAllNatives()
|
|
20
|
+
|
|
21
|
+
namespace jsi = facebook::jsi;
|
|
22
|
+
namespace jni = facebook::jni;
|
|
23
|
+
|
|
24
|
+
// ─────────────────────────── render plane (C-ABI) ───────────────────────────
|
|
25
|
+
|
|
26
|
+
extern "C" {
|
|
27
|
+
void *fressh_terminal_attach(void *window, const char *font_path,
|
|
28
|
+
const char *config_json, const char *shell_id);
|
|
29
|
+
void fressh_terminal_set_shell(void *handle, const char *shell_id);
|
|
30
|
+
void fressh_terminal_set_config(void *handle, const char *config_json);
|
|
31
|
+
void fressh_terminal_draw(void *handle);
|
|
32
|
+
void fressh_terminal_resize(void *handle);
|
|
33
|
+
void fressh_terminal_send_input(void *handle, const uint8_t *data, size_t len);
|
|
34
|
+
void fressh_terminal_destroy(void *handle);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Track the ANativeWindow alongside the Rust handle so we can release the window
|
|
38
|
+
// reference (taken by ANativeWindow_fromSurface) on teardown.
|
|
39
|
+
namespace {
|
|
40
|
+
struct TerminalHandle {
|
|
41
|
+
ANativeWindow *window;
|
|
42
|
+
void *rust;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const char *orNull(JNIEnv *env, jstring s, const char **owned) {
|
|
46
|
+
if (s == nullptr) {
|
|
47
|
+
*owned = nullptr;
|
|
48
|
+
return nullptr;
|
|
49
|
+
}
|
|
50
|
+
*owned = env->GetStringUTFChars(s, nullptr);
|
|
51
|
+
return *owned;
|
|
52
|
+
}
|
|
53
|
+
} // namespace
|
|
54
|
+
|
|
55
|
+
extern "C" JNIEXPORT jlong JNICALL
|
|
56
|
+
Java_com_margelo_nitro_fressh_HybridTerminal_nativeAttach(
|
|
57
|
+
JNIEnv *env, jobject /* this */, jobject surface, jstring font_path,
|
|
58
|
+
jstring config_json, jstring shell_id) {
|
|
59
|
+
ANativeWindow *window = ANativeWindow_fromSurface(env, surface);
|
|
60
|
+
if (window == nullptr) {
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const char *fontOwned = nullptr;
|
|
65
|
+
const char *configOwned = nullptr;
|
|
66
|
+
const char *shellOwned = nullptr;
|
|
67
|
+
const char *font = orNull(env, font_path, &fontOwned);
|
|
68
|
+
const char *config = orNull(env, config_json, &configOwned);
|
|
69
|
+
const char *shell = orNull(env, shell_id, &shellOwned);
|
|
70
|
+
|
|
71
|
+
void *rust = fressh_terminal_attach(window, font, config, shell);
|
|
72
|
+
|
|
73
|
+
if (fontOwned) env->ReleaseStringUTFChars(font_path, fontOwned);
|
|
74
|
+
if (configOwned) env->ReleaseStringUTFChars(config_json, configOwned);
|
|
75
|
+
if (shellOwned) env->ReleaseStringUTFChars(shell_id, shellOwned);
|
|
76
|
+
|
|
77
|
+
if (rust == nullptr) {
|
|
78
|
+
ANativeWindow_release(window);
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
auto *handle = new TerminalHandle{window, rust};
|
|
82
|
+
return reinterpret_cast<jlong>(handle);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
extern "C" JNIEXPORT void JNICALL
|
|
86
|
+
Java_com_margelo_nitro_fressh_HybridTerminal_nativeSetShell(
|
|
87
|
+
JNIEnv *env, jobject /* this */, jlong handle, jstring shell_id) {
|
|
88
|
+
auto *h = reinterpret_cast<TerminalHandle *>(handle);
|
|
89
|
+
if (h == nullptr) return;
|
|
90
|
+
const char *owned = nullptr;
|
|
91
|
+
const char *shell = orNull(env, shell_id, &owned);
|
|
92
|
+
fressh_terminal_set_shell(h->rust, shell);
|
|
93
|
+
if (owned) env->ReleaseStringUTFChars(shell_id, owned);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
extern "C" JNIEXPORT void JNICALL
|
|
97
|
+
Java_com_margelo_nitro_fressh_HybridTerminal_nativeSetConfig(
|
|
98
|
+
JNIEnv *env, jobject /* this */, jlong handle, jstring config_json) {
|
|
99
|
+
auto *h = reinterpret_cast<TerminalHandle *>(handle);
|
|
100
|
+
if (h == nullptr) return;
|
|
101
|
+
const char *owned = nullptr;
|
|
102
|
+
const char *config = orNull(env, config_json, &owned);
|
|
103
|
+
fressh_terminal_set_config(h->rust, config);
|
|
104
|
+
if (owned) env->ReleaseStringUTFChars(config_json, owned);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
extern "C" JNIEXPORT void JNICALL
|
|
108
|
+
Java_com_margelo_nitro_fressh_HybridTerminal_nativeDraw(
|
|
109
|
+
JNIEnv * /* env */, jobject /* this */, jlong handle) {
|
|
110
|
+
auto *h = reinterpret_cast<TerminalHandle *>(handle);
|
|
111
|
+
if (h != nullptr) fressh_terminal_draw(h->rust);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
extern "C" JNIEXPORT void JNICALL
|
|
115
|
+
Java_com_margelo_nitro_fressh_HybridTerminal_nativeResize(
|
|
116
|
+
JNIEnv * /* env */, jobject /* this */, jlong handle) {
|
|
117
|
+
auto *h = reinterpret_cast<TerminalHandle *>(handle);
|
|
118
|
+
if (h != nullptr) fressh_terminal_resize(h->rust);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
extern "C" JNIEXPORT void JNICALL
|
|
122
|
+
Java_com_margelo_nitro_fressh_HybridTerminal_nativeSendInput(
|
|
123
|
+
JNIEnv *env, jobject /* this */, jlong handle, jbyteArray data) {
|
|
124
|
+
auto *h = reinterpret_cast<TerminalHandle *>(handle);
|
|
125
|
+
if (h == nullptr || data == nullptr) return;
|
|
126
|
+
const jsize len = env->GetArrayLength(data);
|
|
127
|
+
if (len <= 0) return;
|
|
128
|
+
jbyte *bytes = env->GetByteArrayElements(data, nullptr);
|
|
129
|
+
fressh_terminal_send_input(h->rust, reinterpret_cast<const uint8_t *>(bytes),
|
|
130
|
+
static_cast<size_t>(len));
|
|
131
|
+
env->ReleaseByteArrayElements(data, bytes, JNI_ABORT);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
extern "C" JNIEXPORT void JNICALL
|
|
135
|
+
Java_com_margelo_nitro_fressh_HybridTerminal_nativeDestroy(
|
|
136
|
+
JNIEnv * /* env */, jobject /* this */, jlong handle) {
|
|
137
|
+
auto *h = reinterpret_cast<TerminalHandle *>(handle);
|
|
138
|
+
if (h != nullptr) {
|
|
139
|
+
fressh_terminal_destroy(h->rust);
|
|
140
|
+
ANativeWindow_release(h->window);
|
|
141
|
+
delete h;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ─────────────────────────── control plane install ───────────────────────────
|
|
146
|
+
|
|
147
|
+
extern "C" JNIEXPORT jboolean JNICALL
|
|
148
|
+
Java_com_margelo_nitro_fressh_ReactNativeTerminalModule_nativeInstallRustCrate(
|
|
149
|
+
JNIEnv * /* env */, jobject /* this */, jlong jsiRuntimePtr,
|
|
150
|
+
jobject callInvokerHolderJavaObj) {
|
|
151
|
+
if (jsiRuntimePtr == 0 || callInvokerHolderJavaObj == nullptr) {
|
|
152
|
+
return JNI_FALSE;
|
|
153
|
+
}
|
|
154
|
+
// RN 0.85 removed CallInvokerHolderImpl.mHybridData; reach the C++ instance via
|
|
155
|
+
// fbjni cthis() instead of the (gone) field.
|
|
156
|
+
using JCallInvokerHolder = facebook::react::CallInvokerHolder;
|
|
157
|
+
auto holderLocal = jni::make_local(callInvokerHolderJavaObj);
|
|
158
|
+
auto holderRef =
|
|
159
|
+
jni::static_ref_cast<JCallInvokerHolder::javaobject>(holderLocal);
|
|
160
|
+
auto *holderCxx = holderRef->cthis();
|
|
161
|
+
std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker =
|
|
162
|
+
holderCxx->getCallInvoker();
|
|
163
|
+
|
|
164
|
+
auto *runtime = reinterpret_cast<jsi::Runtime *>(jsiRuntimePtr);
|
|
165
|
+
NativeShimUniffi::registerModule(*runtime, jsCallInvoker);
|
|
166
|
+
return JNI_TRUE;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ─────────────────────────── Nitro view registration ───────────────────────────
|
|
170
|
+
|
|
171
|
+
// Nitro registers the "Terminal" Fabric component descriptor (plus the HybridObject
|
|
172
|
+
// constructor and the fbjni natives) in registerAllNatives(). It MUST run from this
|
|
173
|
+
// library's JNI_OnLoad — without it, Fabric never learns about the "Terminal"
|
|
174
|
+
// component and falls back to legacy ViewManager interop, which creates the view but
|
|
175
|
+
// silently drops every prop (so `shellId` never reaches HybridTerminal and the
|
|
176
|
+
// renderer draws an unbound/black frame). See ReactNativeTerminalOnLoad.hpp.
|
|
177
|
+
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /* reserved */) {
|
|
178
|
+
return facebook::jni::initialize(
|
|
179
|
+
vm, [] { margelo::nitro::fressh::registerAllNatives(); });
|
|
180
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
package com.margelo.nitro.fressh
|
|
2
|
+
|
|
3
|
+
import android.view.Choreographer
|
|
4
|
+
import android.view.Surface
|
|
5
|
+
import android.view.SurfaceHolder
|
|
6
|
+
import android.view.SurfaceView
|
|
7
|
+
import android.view.View
|
|
8
|
+
import androidx.annotation.Keep
|
|
9
|
+
import com.facebook.proguard.annotations.DoNotStrip
|
|
10
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Native terminal view (Nitro HybridView, the render plane §10). Owns a
|
|
14
|
+
* [SurfaceView]; on surface creation it hands the `ANativeWindow` + the bound
|
|
15
|
+
* `shellId` to the Rust render C-ABI (via JNI) and drives a Choreographer (vsync)
|
|
16
|
+
* loop that draws the shell's durable `Term` from fressh-core's registry. The
|
|
17
|
+
* byte stream never reaches JS. See docs §5/§10.
|
|
18
|
+
*/
|
|
19
|
+
@Keep
|
|
20
|
+
@DoNotStrip
|
|
21
|
+
class HybridTerminal(
|
|
22
|
+
val context: ThemedReactContext,
|
|
23
|
+
) : HybridTerminalSpec() {
|
|
24
|
+
// Plain SurfaceView: the buffer auto-tracks the view bounds. Keeping the grid in
|
|
25
|
+
// lockstep with the on-screen size when the keyboard opens/closes is handled on the
|
|
26
|
+
// Rust side, which polls the *settled* surface size from the draw loop (eglQuerySurface
|
|
27
|
+
// lags the new geometry by a frame, so a one-shot read in surfaceChanged is unreliable
|
|
28
|
+
// — esp. on GROW). The JS side (terminal flex:1 + the toolbar's keyboard-height-driven
|
|
29
|
+
// marginBottom) is what actually resizes this view in both directions. See
|
|
30
|
+
// docs/projects/complete/renderer-mismatched-selection-cutoff-scrollback.md.
|
|
31
|
+
private val surfaceView = SurfaceView(context)
|
|
32
|
+
|
|
33
|
+
/** Opaque pointer to the native render handle (0 = none). */
|
|
34
|
+
private var nativeHandle: Long = 0L
|
|
35
|
+
private var frameCallback: Choreographer.FrameCallback? = null
|
|
36
|
+
|
|
37
|
+
override val view: View = surfaceView
|
|
38
|
+
|
|
39
|
+
// Prop: bundled monospace font file path (no fontconfig on mobile, §6).
|
|
40
|
+
override var fontPath: String = ""
|
|
41
|
+
|
|
42
|
+
// Prop: render config as a JSON blob (physical px), assembled by the JS
|
|
43
|
+
// <Terminal config={...}> wrapper. Carries color scheme, padding, cursor style,
|
|
44
|
+
// bold-is-bright, font size. Live changes reflow the shell — a bonus over
|
|
45
|
+
// desktop alacritty's restart-to-apply. We just forward the string; the renderer
|
|
46
|
+
// parses + diffs it.
|
|
47
|
+
override var configJson: String? = null
|
|
48
|
+
set(value) {
|
|
49
|
+
field = value
|
|
50
|
+
if (nativeHandle != 0L) {
|
|
51
|
+
nativeSetConfig(nativeHandle, value)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Prop: the durable shell to render. Rebinds the native view on change so the
|
|
56
|
+
// same surface can follow a (re)started shell without a remount.
|
|
57
|
+
override var shellId: String? = null
|
|
58
|
+
set(value) {
|
|
59
|
+
field = value
|
|
60
|
+
if (nativeHandle != 0L) {
|
|
61
|
+
nativeSetShell(nativeHandle, value)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
init {
|
|
66
|
+
surfaceView.holder.addCallback(
|
|
67
|
+
object : SurfaceHolder.Callback {
|
|
68
|
+
override fun surfaceCreated(holder: SurfaceHolder) {
|
|
69
|
+
nativeHandle = nativeAttach(holder.surface, resolveFontPath(), configJson, shellId)
|
|
70
|
+
if (nativeHandle != 0L) startRenderLoop()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
|
|
74
|
+
if (nativeHandle != 0L) nativeResize(nativeHandle)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
|
78
|
+
stopRenderLoop()
|
|
79
|
+
if (nativeHandle != 0L) {
|
|
80
|
+
nativeDestroy(nativeHandle)
|
|
81
|
+
nativeHandle = 0L
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The native renderer needs a font *file* path. If RN didn't supply one, fall
|
|
90
|
+
* back to the bundled DejaVu Sans Mono asset, extracted to filesDir once.
|
|
91
|
+
*/
|
|
92
|
+
private fun resolveFontPath(): String {
|
|
93
|
+
if (fontPath.isNotEmpty()) return fontPath
|
|
94
|
+
val out = java.io.File(context.filesDir, "DejaVuSansMono.ttf")
|
|
95
|
+
if (!out.exists()) {
|
|
96
|
+
context.assets.open("fonts/DejaVuSansMono.ttf").use { input ->
|
|
97
|
+
out.outputStream().use { output -> input.copyTo(output) }
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return out.absolutePath
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private fun startRenderLoop() {
|
|
104
|
+
val callback =
|
|
105
|
+
object : Choreographer.FrameCallback {
|
|
106
|
+
override fun doFrame(frameTimeNanos: Long) {
|
|
107
|
+
if (nativeHandle == 0L) return
|
|
108
|
+
nativeDraw(nativeHandle)
|
|
109
|
+
Choreographer.getInstance().postFrameCallback(this)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
frameCallback = callback
|
|
113
|
+
Choreographer.getInstance().postFrameCallback(callback)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private fun stopRenderLoop() {
|
|
117
|
+
frameCallback?.let { Choreographer.getInstance().removeFrameCallback(it) }
|
|
118
|
+
frameCallback = null
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// JNI bridge -> Rust render C-ABI (see android/src/main/cpp/cpp-adapter.cpp).
|
|
122
|
+
private external fun nativeAttach(
|
|
123
|
+
surface: Surface,
|
|
124
|
+
fontPath: String,
|
|
125
|
+
configJson: String?,
|
|
126
|
+
shellId: String?,
|
|
127
|
+
): Long
|
|
128
|
+
|
|
129
|
+
private external fun nativeSetShell(handle: Long, shellId: String?)
|
|
130
|
+
|
|
131
|
+
private external fun nativeSetConfig(handle: Long, configJson: String?)
|
|
132
|
+
|
|
133
|
+
private external fun nativeDraw(handle: Long)
|
|
134
|
+
|
|
135
|
+
private external fun nativeResize(handle: Long)
|
|
136
|
+
|
|
137
|
+
private external fun nativeSendInput(handle: Long, data: ByteArray)
|
|
138
|
+
|
|
139
|
+
private external fun nativeDestroy(handle: Long)
|
|
140
|
+
|
|
141
|
+
companion object {
|
|
142
|
+
init {
|
|
143
|
+
System.loadLibrary("ReactNativeTerminal")
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
package com.margelo.nitro.fressh
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
4
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
5
|
+
import com.facebook.react.bridge.ReactMethod
|
|
6
|
+
import com.facebook.react.module.annotations.ReactModule
|
|
7
|
+
import com.facebook.react.turbomodule.core.interfaces.CallInvokerHolder
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Installs the uniffi control-plane JSI bindings (the `globalThis.NativeShimUniffi`
|
|
11
|
+
* host object the ubrn-generated TS calls). JS invokes [installRustCrate] once at
|
|
12
|
+
* startup (see `src/ssh.ts`), which hands the JSI runtime pointer + the
|
|
13
|
+
* `CallInvokerHolder` to native; `nativeInstallRustCrate` (cpp-adapter.cpp) reaches
|
|
14
|
+
* the C++ CallInvoker via fbjni `cthis()` (RN 0.85 dropped the old field) and calls
|
|
15
|
+
* `NativeShimUniffi::registerModule`. Lives in the SAME .so as the Nitro render
|
|
16
|
+
* plane so both share one `fressh-core` registry. (§8/§10)
|
|
17
|
+
*/
|
|
18
|
+
@ReactModule(name = ReactNativeTerminalModule.NAME)
|
|
19
|
+
class ReactNativeTerminalModule(
|
|
20
|
+
private val reactContext: ReactApplicationContext,
|
|
21
|
+
) : ReactContextBaseJavaModule(reactContext) {
|
|
22
|
+
override fun getName() = NAME
|
|
23
|
+
|
|
24
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
25
|
+
fun installRustCrate(): Boolean {
|
|
26
|
+
val runtimePointer = reactContext.javaScriptContextHolder?.get() ?: return false
|
|
27
|
+
val callInvokerHolder = reactContext.jsCallInvokerHolder ?: return false
|
|
28
|
+
return nativeInstallRustCrate(runtimePointer, callInvokerHolder)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private external fun nativeInstallRustCrate(
|
|
32
|
+
runtimePointer: Long,
|
|
33
|
+
callInvokerHolder: CallInvokerHolder,
|
|
34
|
+
): Boolean
|
|
35
|
+
|
|
36
|
+
companion object {
|
|
37
|
+
const val NAME = "ReactNativeTerminalUniffi"
|
|
38
|
+
|
|
39
|
+
init {
|
|
40
|
+
System.loadLibrary("ReactNativeTerminal")
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
package com.margelo.nitro.fressh
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.ReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.uimanager.ViewManager
|
|
7
|
+
import com.margelo.nitro.fressh.views.HybridTerminalManager
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* ReactPackage for the terminal package. Three jobs:
|
|
11
|
+
* 1. Its mere presence lets React Native autolinking detect this package and
|
|
12
|
+
* build/link its `android/build.gradle` (and thus libReactNativeTerminal.so).
|
|
13
|
+
* 2. Registers the "Terminal" Nitro HybridView's generated ViewManager so Fabric
|
|
14
|
+
* can resolve <Terminal/> (else: "Can't find ViewManager 'Terminal'").
|
|
15
|
+
* 3. Registers [ReactNativeTerminalModule], whose `installRustCrate()` installs
|
|
16
|
+
* the uniffi control-plane JSI bindings (globalThis.NativeShimUniffi).
|
|
17
|
+
* The Nitro native C++ side (HybridObjects) is registered via [ReactNativeTerminalOnLoad]
|
|
18
|
+
* in the static initializer below.
|
|
19
|
+
*/
|
|
20
|
+
class ReactNativeTerminalPackage : ReactPackage {
|
|
21
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> =
|
|
22
|
+
listOf(ReactNativeTerminalModule(reactContext))
|
|
23
|
+
|
|
24
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> =
|
|
25
|
+
listOf(HybridTerminalManager())
|
|
26
|
+
|
|
27
|
+
companion object {
|
|
28
|
+
init {
|
|
29
|
+
ReactNativeTerminalOnLoad.initializeNative()
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
Binary file
|
|
Binary file
|
package/babel.config.js
ADDED
package/cpp/README.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# `cpp/` — native umbrella glue
|
|
2
|
+
|
|
3
|
+
Hand-authored C++ that ties the package's one `.so`/framework together (§8):
|
|
4
|
+
|
|
5
|
+
- the umbrella adapter that registers the TurboModule + Nitro view
|
|
6
|
+
- the Nitro view's C++ that calls **fressh-core's C-ABI** for the render plane
|
|
7
|
+
(`attach(shellId, surface)`, `render_frame`, `send_input`, `detach`)
|
|
8
|
+
|
|
9
|
+
Generated C++ (regenerated, gitignored) lands in `cpp/generated/` (ubrn shim)
|
|
10
|
+
and `nitrogen/generated/` (Nitro view). This dir holds only the hand-written
|
|
11
|
+
adapter sources.
|
|
12
|
+
|
|
13
|
+
> Scaffold stub — empty until the umbrella build is wired (see
|
|
14
|
+
> `../android/CMakeLists.txt`).
|