cj-tauri 1.1.2 → 2.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 +137 -77
- package/bin/cj-tauri.js +287 -287
- package/cjpm.toml +13 -2
- package/cli/cjpm.toml +22 -13
- package/cli/src/doctor.cj +308 -304
- package/cli/src/main.cj +129 -121
- package/cli/src/manifest.cj +632 -0
- package/cli/src/manifest_test.cj +87 -0
- package/cli/src/project.cj +770 -754
- package/cli/src/resolve.cj +348 -348
- package/cli/src/scaffold.cj +46 -38
- package/cli/src/sync.cj +526 -0
- package/cli/src/util.cj +9 -0
- package/cli/templates/app/README.md +30 -59
- package/cli/templates/app/{ui → assets/ui}/index.html +2 -0
- package/cli/templates/app/cj-tauri.toml +32 -0
- package/cli/templates/app/cjpm.toml +21 -4
- package/cli/templates/app/src/main.cj +29 -16
- package/cli/templates/app-react/README.md +34 -45
- package/cli/templates/app-react/{ui → assets/ui}/index.html +25 -25
- package/cli/templates/app-react/{ui → assets/ui}/src/App.css +67 -67
- package/cli/templates/app-react/{ui → assets/ui}/src/App.jsx +124 -124
- package/cli/templates/app-react/cj-tauri.toml +33 -0
- package/cli/templates/app-react/cjpm.toml +21 -4
- package/cli/templates/app-react/src/main.cj +18 -29
- package/cli/templates/app-vue/README.md +34 -38
- package/cli/templates/app-vue/{ui → assets/ui}/index.html +25 -25
- package/cli/templates/app-vue/{ui → assets/ui}/src/App.vue +176 -176
- package/cli/templates/app-vue/cj-tauri.toml +33 -0
- package/cli/templates/app-vue/cjpm.toml +21 -4
- package/cli/templates/app-vue/src/main.cj +18 -29
- package/cli/templates/{ohos-pc → ohos-skeleton}/README.md +5 -0
- package/cli/templates/{ohos-pc → ohos-skeleton}/build-hap.sh +15 -0
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/cjpm.toml +8 -0
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/dialog_host.cj +217 -0
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/index.cj +527 -0
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/module.json5 +8 -0
- package/native/build_win.bat +62 -62
- package/native/core/bridge_core.c +1681 -1626
- package/native/core/bridge_core.h +675 -618
- package/native/core/bridge_js.h +108 -108
- package/native/platforms/harmonyos/README.md +115 -6
- package/native/platforms/linux/bridge_linux.c +2787 -2485
- package/native/platforms/windows/bridge_win.c +3740 -3248
- package/native/tests/gtk_threading_probe.c +143 -143
- package/native/tests/test_bridge_core.c +1339 -1228
- package/package.json +53 -51
- package/prebuilt/linux-x64/cj-tauri +0 -0
- package/src/api_system.cj +67 -67
- package/src/app.cj +325 -940
- package/src/app_events.cj +166 -0
- package/src/app_host_ops.cj +287 -0
- package/src/app_run.cj +283 -0
- package/src/app_spec.cj +76 -0
- package/src/arkweb_menu.cj +216 -0
- package/src/arkweb_script.cj +0 -12
- package/src/capability.cj +409 -409
- package/src/clipboard_bridge.cj +178 -0
- package/src/dialog_spec.cj +79 -0
- package/src/dragdrop.cj +26 -26
- package/src/embedded_assets.cj +98 -98
- package/src/host.cj +752 -604
- package/src/host_arkweb.cj +201 -17
- package/src/host_webkit.cj +663 -548
- package/src/host_webview2.cj +663 -548
- package/src/hotkey.cj +66 -66
- package/src/ipc_hub.cj +337 -337
- package/src/ipc_message.cj +286 -286
- package/src/launch.cj +62 -0
- package/src/logging.cj +127 -127
- package/src/menu.cj +197 -197
- package/src/platform/platform_app_context.cj +77 -0
- package/src/platform/platform_assets.cj +33 -0
- package/src/platform/platform_discover.cj +176 -0
- package/src/platform/platform_env.cj +29 -0
- package/src/platform/platform_feature.cj +31 -0
- package/src/platform/platform_page_source.cj +87 -0
- package/src/platform/platform_target_info.cj +32 -0
- package/src/platform_host_provider.cj +87 -0
- package/src/plugin.cj +333 -299
- package/src/plugins/clipboard/plugin_clipboard.cj +152 -144
- package/src/plugins/dialog/plugin_dialog.cj +209 -209
- package/src/plugins/fs/plugin_fs.cj +105 -1
- package/src/plugins/http/http_backend.cj +131 -0
- package/src/plugins/http/http_desktop.cj +195 -0
- package/src/plugins/http/http_ohos.cj +204 -0
- package/src/plugins/http/plugin_http.cj +310 -459
- package/src/plugins/log/plugin_log.cj +105 -105
- package/src/plugins/menu/plugin_menu.cj +8 -0
- package/src/plugins/notification/plugin_notification.cj +154 -146
- package/src/plugins/serial/plugin_serial.cj +17 -1
- package/src/plugins/serial/serial_bridge.cj +9 -1
- package/src/plugins/serial/serial_manager_ohos.cj +92 -0
- package/src/plugins/serial/serial_ohos.cj +529 -0
- package/src/plugins/shell/plugin_shell.cj +29 -1
- package/src/plugins/sqlite/plugin_sqlite.cj +8 -0
- package/src/plugins/ws/plugin_ws.cj +488 -470
- package/src/process_hidden.cj +174 -174
- package/src/scope.cj +391 -391
- package/src/shell_event.cj +69 -69
- package/src/tests/app_spec_test.cj +126 -0
- package/src/tests/capability_test.cj +243 -243
- package/src/tests/dialog_spec_test.cj +73 -0
- package/src/tests/fake_host.cj +201 -186
- package/src/tests/ipc_hub_test.cj +487 -487
- package/src/tests/logging_test.cj +62 -62
- package/src/tests/platform_contract_test.cj +251 -0
- package/src/tests/plugin_clipboard_test.cj +222 -222
- package/src/tests/plugin_dialog_test.cj +237 -237
- package/src/tests/plugin_fs_test.cj +207 -0
- package/src/tests/plugin_http_test.cj +124 -124
- package/src/tests/plugin_notification_test.cj +205 -205
- package/src/tests/plugin_serial_test.cj +48 -0
- package/src/tests/plugin_test.cj +66 -3
- package/src/tests/plugin_ws_test.cj +40 -40
- package/src/tests/process_hidden_test.cj +129 -129
- package/src/tests/scope_test.cj +350 -350
- package/src/tests/system_host_test.cj +99 -81
- package/src/tests/urlutil_test.cj +53 -53
- package/src/tests/version_test.cj +75 -75
- package/src/tests/window_registry_test.cj +123 -123
- package/src/tray.cj +90 -90
- package/src/urlutil.cj +159 -159
- package/src/version.cj +44 -44
- package/src/window.cj +51 -51
- package/src/window_registry.cj +116 -116
- package/cli/templates/ohos-pc/entry/src/main/cangjie/index.cj +0 -167
- package/prebuilt/win32-x64/cj-tauri.exe +0 -0
- package/src/host_factory.cj +0 -48
- /package/cli/templates/app/{capabilities → assets/capabilities}/default.json +0 -0
- /package/cli/templates/app-react/{capabilities → assets/capabilities}/default.json +0 -0
- /package/cli/templates/app-react/{ui → assets/ui}/package.json +0 -0
- /package/cli/templates/app-react/{ui → assets/ui}/src/main.jsx +0 -0
- /package/cli/templates/app-react/{ui → assets/ui}/vite.config.js +0 -0
- /package/cli/templates/app-vue/{capabilities → assets/capabilities}/default.json +0 -0
- /package/cli/templates/app-vue/{ui → assets/ui}/package.json +0 -0
- /package/cli/templates/app-vue/{ui → assets/ui}/src/main.js +0 -0
- /package/cli/templates/app-vue/{ui → assets/ui}/vite.config.js +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/app.json5 +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/element/string.json +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/media/background.png +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/media/foreground.png +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/media/layered_image.json +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/build-profile.json5 +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/build-profile.json5 +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/hvigorfile.ts +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/oh-package.json5 +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/ability_stage.cj +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/app_host.cj +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/main_ability.cj +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/element/color.json +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/element/string.json +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/background.png +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/foreground.png +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/layered_image.json +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/startIcon.png +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/profile/main_pages.json +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/rawfile/capabilities.json +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/rawfile/index.html +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/hvigor/hvigor-config.json5 +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/hvigorfile.ts +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/oh-package.json5 +0 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
package cjTauri.plugins.serial
|
|
2
|
+
|
|
3
|
+
// 桌面侧看不到这个文件里的 `@When[env == "ohos"]` 函数体,`ArrayList` 就成「未使用」——
|
|
4
|
+
// 按 AGENTS §4「跨平台文件别按单平台警告去清 import」,给 import 加同款门禁,而不是删掉它。
|
|
5
|
+
@When[env == "ohos"]
|
|
6
|
+
import std.collection.*
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 鸿蒙侧 USB 串口后端:**如实降级,不做互操作**
|
|
10
|
+
*
|
|
11
|
+
* ## 为什么这里是空的(2026-10-24 真机 A/B 实测,别再徒手重试)
|
|
12
|
+
*
|
|
13
|
+
* 原本的落法是「仓颉侧经 `ohos.ark_interop` 调 `@ohos.usbManager.serial`」,三组对照把它否掉了:
|
|
14
|
+
*
|
|
15
|
+
* | 产物 | 内容 | 真机结果 |
|
|
16
|
+
* | --- | --- | --- |
|
|
17
|
+
* | 有互操作、真去用 | `spawn` 出 owner 线程建 `JSRuntime()` | 启动即 `signal:6`(SIGABRT),日志停在 `startApp begin`,`initScripts` 都没打印 |
|
|
18
|
+
* | 只有互操作、从不执行 | 同名符号全留,owner 线程刻意不起 | **应用正常启动**,页面跑完自检(`pidof` 有、`page end label=main`) |
|
|
19
|
+
* | 互操作整段摘掉 | 本文件上一版的空桩 | 同样正常启动 |
|
|
20
|
+
*
|
|
21
|
+
* 第三组证明「链接 `libohos.ark_interop.so` 本身无害」(`NEEDED` 里两个库、4 个 JSRuntime 符号都在,
|
|
22
|
+
* 应用照跑);**真正 abort 的是执行 `JSRuntime()`**。再补一个探针把创建点挪到 UI 线程
|
|
23
|
+
* (`onWindowStageCreate`,与 ArkTS 同一条线程)——只打出 `runtime probe begin [ui-thread]`,
|
|
24
|
+
* 结论行永远没出现、进程当场消失。所以不是「Cangjie 轻量线程没有 VM」,
|
|
25
|
+
* 而是**纯仓颉进程里根本没有 ArkTS VM**;线程换哪儿都一样。
|
|
26
|
+
*
|
|
27
|
+
* 结论:`serialManager` 只能由**真正的 ArkTS 侧**调用(混合工程,或应用自己宿主 ArkTS 模块),
|
|
28
|
+
* 不能关在框架插件的叶子内。ADR 见 `docs/设计决策记录.md` D-022 的 2026-10-24 结论段。
|
|
29
|
+
*
|
|
30
|
+
* ## 所以这里的选择
|
|
31
|
+
*
|
|
32
|
+
* 保持插件导出面(`serialOpen` 等已按句柄空间分流到这里),但一律**如实报不可用**——
|
|
33
|
+
* 既不假装成功,也不让「没后台」变成「应用起不来」。POSIX 的 `/dev/ttyS*` 通路不受影响
|
|
34
|
+
* (`serialOpen` 先试 POSIX,失败才落到这儿)。
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/** 降级原因(命令层原样回给前端,便于页面说清楚为什么不能开 USB 串口) */
|
|
38
|
+
@When[env == "ohos"]
|
|
39
|
+
const OHOS_USB_UNAVAILABLE: String =
|
|
40
|
+
"USB 串口在鸿蒙上需要 ArkTS 侧调用 serialManager:纯仓颉进程里没有 ArkTS VM," +
|
|
41
|
+
"建 JSRuntime 会让进程 SIGABRT(真机 A/B 已证);请改用 /dev/ttyS* 或走 ArkTS 宿主"
|
|
42
|
+
|
|
43
|
+
/** 句柄是否属于 USB 串口空间——降级后永远 false,`serialRead/Write/Close` 全部留在 POSIX 侧 */
|
|
44
|
+
@When[env == "ohos"]
|
|
45
|
+
public func usbSerialIsHandle(h: Int64): Bool {
|
|
46
|
+
let _ = h
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** 枚举 USB 串口:降级后恒空(「没有 USB 串口」与「后端不可用」在命令层同形,原因进错误槽) */
|
|
51
|
+
@When[env == "ohos"]
|
|
52
|
+
public func usbSerialListNames(): ArrayList<String> {
|
|
53
|
+
return ArrayList<String>()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** 打开 USB 串口:恒回 -3(打不开/未授权)并写明原因 */
|
|
57
|
+
@When[env == "ohos"]
|
|
58
|
+
public func usbSerialOpenByPath(path: String, baud: Int32, dataBits: Int32, parity: Int32,
|
|
59
|
+
stopBits: Int32): Int64 {
|
|
60
|
+
let _ = path
|
|
61
|
+
let _ = baud
|
|
62
|
+
let _ = dataBits
|
|
63
|
+
let _ = parity
|
|
64
|
+
let _ = stopBits
|
|
65
|
+
serialOhosSetError(OHOS_USB_UNAVAILABLE)
|
|
66
|
+
return -3
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** 读:不会有句柄走到这里(`usbSerialIsHandle` 恒 false),保留签名为了上层不分叉 */
|
|
70
|
+
@When[env == "ohos"]
|
|
71
|
+
public func usbSerialReadHandle(h: Int64, maxBytes: Int32, timeoutMs: Int32): Array<UInt8> {
|
|
72
|
+
let _ = h
|
|
73
|
+
let _ = maxBytes
|
|
74
|
+
let _ = timeoutMs
|
|
75
|
+
return Array<UInt8>(0, repeat: 0)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** 写:同上,恒 0 字节 */
|
|
79
|
+
@When[env == "ohos"]
|
|
80
|
+
public func usbSerialWriteHandle(h: Int64, data: Array<UInt8>, timeoutMs: Int32): Int32 {
|
|
81
|
+
let _ = h
|
|
82
|
+
let _ = data
|
|
83
|
+
let _ = timeoutMs
|
|
84
|
+
return 0
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** 关句柄:同上,恒 -4 */
|
|
88
|
+
@When[env == "ohos"]
|
|
89
|
+
public func usbSerialCloseHandle(h: Int64): Int32 {
|
|
90
|
+
let _ = h
|
|
91
|
+
return -4
|
|
92
|
+
}
|
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
package cjTauri.plugins.serial
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ohos 侧 serial 真实现(批次 4,issue #5):NDK FFI 直连 musl libc 的 POSIX 串口面。
|
|
5
|
+
* 前身是 P-1 桩(serial_ohos_stub.cj,已删);写法照剪贴板那次的「NDK FFI 直连」模式
|
|
6
|
+
* (`src/clipboard_bridge.cj`),不经 C 桥、不加能力位。
|
|
7
|
+
*
|
|
8
|
+
* 为什么不引 serialport 式的现成库:std / stdx 都没有串口与 termios 绑定;同类项目 sscom
|
|
9
|
+
* 实测 serialport 依赖 nix/libudev 在 OHOS target 编不过,只能手写 POSIX——本文件同理,
|
|
10
|
+
* termios 结构体按 musl(aarch64 linux)布局手工按字节偏移读写,不依赖 @C 结构体映射。
|
|
11
|
+
*
|
|
12
|
+
* 边界与桌面两端的差异(对齐 RFC §4.6.4 P-2 的口径):
|
|
13
|
+
* - 桌面侧「真正的白名单在 C 桥 core」;ohos 没有 C 桥,本文件**就是**安全边界,
|
|
14
|
+
* open 前必须再过一次 `serialPathLooksAllowed`(仓颉侧镜像,plugin_serial.cj)——
|
|
15
|
+
* 页面可控字符串绝不能只靠上层命令层的校验。
|
|
16
|
+
* - 错误明细是包级共享槽(仓颉没有线程局部),Mutex 守着;并发失败时可能被覆盖,
|
|
17
|
+
* 代价只是错误文案不准,与桌面「线程局部」的语义差异如实记在这里。
|
|
18
|
+
* - 读超时用 poll(有数据才 read,与桌面 Linux 桥同口径);写按预算轮转。
|
|
19
|
+
* - 设备节点访问在真机上可能要 DDK/USB 权限甚至系统签名(sscom 同款风险),装不上 /
|
|
20
|
+
* 打不开时本实现如实报错,不伪装成功。
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
@When[env == "ohos"]
|
|
24
|
+
import std.collection.*
|
|
25
|
+
|
|
26
|
+
// std.sync 只被 ohos 门禁代码用(Mutex 状态槽),import 随之门禁——桌面侧整段摘除
|
|
27
|
+
@When[env == "ohos"]
|
|
28
|
+
import std.sync.*
|
|
29
|
+
|
|
30
|
+
@When[env == "ohos"]
|
|
31
|
+
import std.time.*
|
|
32
|
+
|
|
33
|
+
@When[env == "ohos"]
|
|
34
|
+
import std.fs.*
|
|
35
|
+
|
|
36
|
+
@When[env == "ohos"]
|
|
37
|
+
import std.sort.*
|
|
38
|
+
|
|
39
|
+
// ---- musl libc 的 POSIX 串口面(@C foreign 直连;符号都在 libc,无需 [links] 追加) ----
|
|
40
|
+
|
|
41
|
+
/** open 只按双参声明:aarch64(AAPCS64)上可变参与固定参的整型传参相同, musl 的 open 直接可用 */
|
|
42
|
+
@When[env == "ohos"]
|
|
43
|
+
@C
|
|
44
|
+
foreign func open(path: CString, flags: Int32): Int32
|
|
45
|
+
|
|
46
|
+
@When[env == "ohos"]
|
|
47
|
+
@C
|
|
48
|
+
foreign func close(fd: Int32): Int32
|
|
49
|
+
|
|
50
|
+
@When[env == "ohos"]
|
|
51
|
+
@C
|
|
52
|
+
foreign func read(fd: Int32, buf: CPointer<UInt8>, count: UInt64): Int64
|
|
53
|
+
|
|
54
|
+
@When[env == "ohos"]
|
|
55
|
+
@C
|
|
56
|
+
foreign func write(fd: Int32, buf: CPointer<UInt8>, count: UInt64): Int64
|
|
57
|
+
|
|
58
|
+
/** termios 按 64 字节裸缓冲传递(musl aarch64 布局,实际 60 字节) */
|
|
59
|
+
@When[env == "ohos"]
|
|
60
|
+
@C
|
|
61
|
+
foreign func tcgetattr(fd: Int32, termios: CPointer<UInt8>): Int32
|
|
62
|
+
|
|
63
|
+
@When[env == "ohos"]
|
|
64
|
+
@C
|
|
65
|
+
foreign func tcsetattr(fd: Int32, actions: Int32, termios: CPointer<UInt8>): Int32
|
|
66
|
+
|
|
67
|
+
@When[env == "ohos"]
|
|
68
|
+
@C
|
|
69
|
+
foreign func cfsetispeed(termios: CPointer<UInt8>, speed: UInt32): Int32
|
|
70
|
+
|
|
71
|
+
@When[env == "ohos"]
|
|
72
|
+
@C
|
|
73
|
+
foreign func cfsetospeed(termios: CPointer<UInt8>, speed: UInt32): Int32
|
|
74
|
+
|
|
75
|
+
/** pollfd 按 8 字节裸缓冲:fd(Int32) | events(Int16) | revents(Int16) */
|
|
76
|
+
@When[env == "ohos"]
|
|
77
|
+
@C
|
|
78
|
+
foreign func poll(fds: CPointer<UInt8>, nfds: UInt64, timeout: Int32): Int32
|
|
79
|
+
|
|
80
|
+
/** errno 与 strerror:给 serialLastError 一句人话(strerror 返回静态串,不释放) */
|
|
81
|
+
@When[env == "ohos"]
|
|
82
|
+
@C
|
|
83
|
+
foreign func __errno_location(): CPointer<Int32>
|
|
84
|
+
|
|
85
|
+
@When[env == "ohos"]
|
|
86
|
+
@C
|
|
87
|
+
foreign func strerror(errnum: Int32): CString
|
|
88
|
+
|
|
89
|
+
// ---- Linux/asm-generic 常量(与 sscom 的取值一字不差) ----
|
|
90
|
+
|
|
91
|
+
// open flags
|
|
92
|
+
const OHOS_O_RDWR: Int32 = 0x2
|
|
93
|
+
const OHOS_O_NOCTTY: Int32 = 0x100
|
|
94
|
+
|
|
95
|
+
// tcsetattr actions:TCSANOW
|
|
96
|
+
const OHOS_TCSANOW: Int32 = 0
|
|
97
|
+
|
|
98
|
+
// cflag 位
|
|
99
|
+
const OHOS_CSIZE: UInt32 = 0x30
|
|
100
|
+
const OHOS_CS8: UInt32 = 0x30
|
|
101
|
+
const OHOS_CSTOPB: UInt32 = 0x40
|
|
102
|
+
const OHOS_CREAD: UInt32 = 0x80
|
|
103
|
+
const OHOS_PARENB: UInt32 = 0x100
|
|
104
|
+
const OHOS_PARODD: UInt32 = 0x200
|
|
105
|
+
const OHOS_CLOCAL: UInt32 = 0x800
|
|
106
|
+
|
|
107
|
+
// iflag 原始位(cfmakeraw 要清的):IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON
|
|
108
|
+
const OHOS_IFLAG_RAW: UInt32 = 0x5eb
|
|
109
|
+
|
|
110
|
+
// lflag 原始位:ECHO|ICANON|ISIG|IEXTEN
|
|
111
|
+
const OHOS_LFLAG_RAW: UInt32 = 0x800b
|
|
112
|
+
|
|
113
|
+
// poll 事件
|
|
114
|
+
const OHOS_POLLIN: Int16 = 0x1
|
|
115
|
+
const OHOS_POLLOUT: Int16 = 0x4
|
|
116
|
+
// errno:EAGAIN
|
|
117
|
+
const OHOS_EAGAIN: Int32 = 11
|
|
118
|
+
|
|
119
|
+
/** termios 结构体里 speed 字段的字节偏移(musl aarch64:c_ispeed@52 / c_ospeed@56) */
|
|
120
|
+
const OHOS_TERMIOS_ISPEED: Int64 = 52
|
|
121
|
+
const OHOS_TERMIOS_OSPEED: Int64 = 56
|
|
122
|
+
|
|
123
|
+
/** c_cc 里 VTIME / VMIN 的下标(Linux 布局);poll+read 模式下两个都置 0 */
|
|
124
|
+
const OHOS_VTIME: Int64 = 5
|
|
125
|
+
const OHOS_VMIN: Int64 = 6
|
|
126
|
+
|
|
127
|
+
/** 波特率 → termios 速度常量(asm-generic:B38400 以下连续,往上 0x1000+n);
|
|
128
|
+
* 档位与 plugin_serial.cj 的 SERIAL_BAUDS 对齐(那层已拦过一次,这里兜底)。
|
|
129
|
+
* public:单测要直接构造(跨包可见性,AGENTS §2) */
|
|
130
|
+
public func serialOhosBaudConst(baud: Int32): Option<UInt32> {
|
|
131
|
+
let low: Option<UInt32> = match (baud) {
|
|
132
|
+
case 300 => Some(0x7)
|
|
133
|
+
case 600 => Some(0x8)
|
|
134
|
+
case 1200 => Some(0x9)
|
|
135
|
+
case 2400 => Some(0xb)
|
|
136
|
+
case 4800 => Some(0xc)
|
|
137
|
+
case 9600 => Some(0xd)
|
|
138
|
+
case 19200 => Some(0xe)
|
|
139
|
+
case 38400 => Some(0xf)
|
|
140
|
+
case _ => None
|
|
141
|
+
}
|
|
142
|
+
if (let Some(v) <- low) {
|
|
143
|
+
return low
|
|
144
|
+
}
|
|
145
|
+
match (baud) {
|
|
146
|
+
case 57600 => Some(0x1001)
|
|
147
|
+
case 115200 => Some(0x1002)
|
|
148
|
+
case 230400 => Some(0x1003)
|
|
149
|
+
case 460800 => Some(0x1004)
|
|
150
|
+
case 500000 => Some(0x1005)
|
|
151
|
+
case 921600 => Some(0x1006)
|
|
152
|
+
case 1000000 => Some(0x1008)
|
|
153
|
+
case _ => None
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ---- 错误明细(包级共享槽,Mutex 守护;见文件头与桌面线程局部语义的差异说明) ----
|
|
158
|
+
// 引用 ohos 专属 foreign(strerror / __errno_location / poll)的辅助一律带门禁,
|
|
159
|
+
// 否则桌面构建在链接期找不到符号(AGENTS §4:跨平台文件别按单平台的 warning 清 import)
|
|
160
|
+
|
|
161
|
+
@When[env == "ohos"]
|
|
162
|
+
let OHOS_SERIAL_ERR_MUTEX = Mutex()
|
|
163
|
+
|
|
164
|
+
@When[env == "ohos"]
|
|
165
|
+
var ohosSerialLastError = ""
|
|
166
|
+
|
|
167
|
+
@When[env == "ohos"]
|
|
168
|
+
func serialOhosSetError(msg: String): Unit {
|
|
169
|
+
OHOS_SERIAL_ERR_MUTEX.lock()
|
|
170
|
+
ohosSerialLastError = msg
|
|
171
|
+
OHOS_SERIAL_ERR_MUTEX.unlock()
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@When[env == "ohos"]
|
|
175
|
+
func serialOhosErrno(): Int32 {
|
|
176
|
+
unsafe {
|
|
177
|
+
let p = __errno_location()
|
|
178
|
+
if (p.isNull()) {
|
|
179
|
+
return 0
|
|
180
|
+
}
|
|
181
|
+
return p.read(0)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** 最近一次 errno 的人话(strerror 返回静态串,不释放);调用处无需 unsafe */
|
|
186
|
+
@When[env == "ohos"]
|
|
187
|
+
func serialOhosErrnoText(): String {
|
|
188
|
+
unsafe {
|
|
189
|
+
return strerror(serialOhosErrno()).toString()
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ---- termios 裸缓冲的读写辅助(aarch64 是小端;public 供单测直接构造,AGENTS §2) ----
|
|
194
|
+
|
|
195
|
+
public func serialOhosGetU32(t: CPointer<UInt8>, off: Int64): UInt32 {
|
|
196
|
+
unsafe {
|
|
197
|
+
let b0 = UInt32(t.read(off))
|
|
198
|
+
let b1 = UInt32(t.read(off + 1)) << 8
|
|
199
|
+
let b2 = UInt32(t.read(off + 2)) << 16
|
|
200
|
+
let b3 = UInt32(t.read(off + 3)) << 24
|
|
201
|
+
return b0 | b1 | b2 | b3
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
public func serialOhosPutU32(t: CPointer<UInt8>, off: Int64, v: UInt32): Unit {
|
|
206
|
+
unsafe {
|
|
207
|
+
t.write(off, UInt8(v & 0xff))
|
|
208
|
+
t.write(off + 1, UInt8((v >> 8) & 0xff))
|
|
209
|
+
t.write(off + 2, UInt8((v >> 16) & 0xff))
|
|
210
|
+
t.write(off + 3, UInt8((v >> 24) & 0xff))
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** poll 单 fd 单事件:>0 就绪,0 超时,<0 出错(errno 已置) */
|
|
215
|
+
@When[env == "ohos"]
|
|
216
|
+
func serialOhosPollOne(fd: Int32, events: Int16, timeoutMs: Int32): Int32 {
|
|
217
|
+
let pfd = LibC.malloc<UInt8>(count: 8)
|
|
218
|
+
unsafe {
|
|
219
|
+
// fd 低 4 字节小端写入;events 高位偏移 4;revents(偏移 6)由内核填,预清零
|
|
220
|
+
pfd.write(0, UInt8(Int32(fd) & 0xff))
|
|
221
|
+
pfd.write(1, UInt8((fd >> 8) & 0xff))
|
|
222
|
+
pfd.write(2, UInt8((fd >> 16) & 0xff))
|
|
223
|
+
pfd.write(3, UInt8((fd >> 24) & 0xff))
|
|
224
|
+
pfd.write(4, UInt8(UInt16(events) & 0xff))
|
|
225
|
+
pfd.write(5, UInt8((UInt16(events) >> 8) & 0xff))
|
|
226
|
+
pfd.write(6, UInt8(0))
|
|
227
|
+
pfd.write(7, UInt8(0))
|
|
228
|
+
}
|
|
229
|
+
var r: Int32 = 0
|
|
230
|
+
unsafe {
|
|
231
|
+
r = poll(pfd, 1, timeoutMs)
|
|
232
|
+
}
|
|
233
|
+
unsafe {
|
|
234
|
+
LibC.free(pfd)
|
|
235
|
+
}
|
|
236
|
+
return r
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* 打开并配置串口:成功返回 >0 句柄(fd+1,保证恒 >0),失败负码:
|
|
241
|
+
* -1 参数 / 白名单、-3 open 或 termios 配置失败(明细在 serialLastError())。
|
|
242
|
+
* 线程安全:fd 由调用方独占使用;重复 open 同一路径是两次独立连接(与桌面同形)。
|
|
243
|
+
*/
|
|
244
|
+
@When[env == "ohos"]
|
|
245
|
+
public func serialOpen(path: String, baud: Int32, dataBits: Int32, parity: Int32,
|
|
246
|
+
stopBits: Int32): Int64 {
|
|
247
|
+
// ohos 没有桌面 C 桥那层白名单,本函数就是边界:页面可控路径必须在这里再拦一次
|
|
248
|
+
if (!serialPathLooksAllowed(path)) {
|
|
249
|
+
serialOhosSetError("path \"${path}\" 不在允许的设备名单内(serialPathLooksAllowed)")
|
|
250
|
+
return -1
|
|
251
|
+
}
|
|
252
|
+
if (serialOhosBaudConst(baud).isNone()) {
|
|
253
|
+
serialOhosSetError("不支持的波特率 ${baud}")
|
|
254
|
+
return -1
|
|
255
|
+
}
|
|
256
|
+
if (dataBits < 5 || dataBits > 8) {
|
|
257
|
+
serialOhosSetError("dataBits 只收 5..8(拿到 ${dataBits})")
|
|
258
|
+
return -1
|
|
259
|
+
}
|
|
260
|
+
if (parity < 0 || parity > 2) {
|
|
261
|
+
serialOhosSetError("parity 只收 0|1|2(拿到 ${parity})")
|
|
262
|
+
return -1
|
|
263
|
+
}
|
|
264
|
+
if (stopBits != 1 && stopBits != 2) {
|
|
265
|
+
serialOhosSetError("stopBits 只收 1|2(拿到 ${stopBits})")
|
|
266
|
+
return -1
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
var fd: Int32 = 0
|
|
270
|
+
unsafe {
|
|
271
|
+
let cpath = LibC.mallocCString(path)
|
|
272
|
+
fd = open(cpath, OHOS_O_RDWR | OHOS_O_NOCTTY)
|
|
273
|
+
LibC.free(cpath)
|
|
274
|
+
}
|
|
275
|
+
if (fd < 0) {
|
|
276
|
+
// POSIX 打不开就走 serialManager(serial_manager_ohos.cj)。普通三方应用对 /dev/bus
|
|
277
|
+
// 整体不可达(SELinux,连 hdc shell 都进不去),seriaManager 由系统代管设备节点、
|
|
278
|
+
// 用户点一次授权即可——这是三方应用碰 USB 串口的正路,不是「降级兜底」。
|
|
279
|
+
let posixErr = serialOhosErrnoText()
|
|
280
|
+
let usbHandle = usbSerialOpenByPath(path, baud, dataBits, parity, stopBits)
|
|
281
|
+
if (usbHandle > 0) {
|
|
282
|
+
return usbHandle
|
|
283
|
+
}
|
|
284
|
+
// 两条路都失败:POSIX 的 errno 与 USB 侧原因一并写清,否则只看到「端口表里没有」
|
|
285
|
+
serialOhosSetError("open failed: POSIX ${posixErr};USB ${serialLastError()}")
|
|
286
|
+
return -3
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
let t = LibC.malloc<UInt8>(count: 64)
|
|
290
|
+
unsafe {
|
|
291
|
+
for (i in 0..64) {
|
|
292
|
+
t.write(i, 0)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
var rc: Int32 = 0
|
|
296
|
+
unsafe {
|
|
297
|
+
rc = tcgetattr(fd, t)
|
|
298
|
+
}
|
|
299
|
+
if (rc != 0) {
|
|
300
|
+
serialOhosSetError("tcgetattr failed: ${serialOhosErrnoText()}")
|
|
301
|
+
unsafe {
|
|
302
|
+
close(fd)
|
|
303
|
+
LibC.free(t)
|
|
304
|
+
}
|
|
305
|
+
return -3
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// cfmakeraw 等价:清 iflag/lflag 原始位,oflag/lflag 归零,cflag 重设数据位/校验/停止位
|
|
309
|
+
// (取反用「异或全 1 掩码」:仓颉没有 ~ 运算符,2026-10-24 编译实测)
|
|
310
|
+
let iflag = serialOhosGetU32(t, 0) & (OHOS_IFLAG_RAW ^ 0xFFFF_FFFF)
|
|
311
|
+
serialOhosPutU32(t, 0, iflag)
|
|
312
|
+
serialOhosPutU32(t, 4, 0)
|
|
313
|
+
var cflag = serialOhosGetU32(t, 8) & ((OHOS_CSIZE | OHOS_PARENB | OHOS_PARODD | OHOS_CSTOPB) ^ 0xFFFF_FFFF)
|
|
314
|
+
cflag |= OHOS_CREAD | OHOS_CLOCAL
|
|
315
|
+
// dataBits:CS5..CS8 = 0x00/0x10/0x20/0x30
|
|
316
|
+
cflag |= UInt32(dataBits - 5) << 4
|
|
317
|
+
if (parity == 1) {
|
|
318
|
+
cflag |= OHOS_PARODD | OHOS_PARENB
|
|
319
|
+
} else if (parity == 2) {
|
|
320
|
+
cflag |= OHOS_PARENB
|
|
321
|
+
}
|
|
322
|
+
if (stopBits == 2) {
|
|
323
|
+
cflag |= OHOS_CSTOPB
|
|
324
|
+
}
|
|
325
|
+
serialOhosPutU32(t, 8, cflag)
|
|
326
|
+
// lflag 清原始位(c_line@16 与 c_cc 其余位保留,buffer 已按 tcgetattr 的返回初始化)
|
|
327
|
+
serialOhosPutU32(t, 12, serialOhosGetU32(t, 12) & (OHOS_LFLAG_RAW ^ 0xFFFF_FFFF))
|
|
328
|
+
// VTIME / VMIN 置 0:read 不自带阻塞预算,超时由 poll 负责(与桌面 Linux 桥同口径)
|
|
329
|
+
unsafe {
|
|
330
|
+
t.write(17 + OHOS_VTIME, UInt8(0))
|
|
331
|
+
t.write(17 + OHOS_VMIN, UInt8(0))
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
match (serialOhosBaudConst(baud)) {
|
|
335
|
+
case Some(speed) =>
|
|
336
|
+
unsafe {
|
|
337
|
+
cfsetispeed(t, speed)
|
|
338
|
+
cfsetospeed(t, speed)
|
|
339
|
+
}
|
|
340
|
+
case None => () // 上面已拦,不可能走到
|
|
341
|
+
}
|
|
342
|
+
unsafe {
|
|
343
|
+
rc = tcsetattr(fd, OHOS_TCSANOW, t)
|
|
344
|
+
}
|
|
345
|
+
if (rc != 0) {
|
|
346
|
+
serialOhosSetError("tcsetattr failed: ${serialOhosErrnoText()}")
|
|
347
|
+
unsafe {
|
|
348
|
+
close(fd)
|
|
349
|
+
LibC.free(t)
|
|
350
|
+
}
|
|
351
|
+
return -3
|
|
352
|
+
}
|
|
353
|
+
unsafe {
|
|
354
|
+
LibC.free(t)
|
|
355
|
+
}
|
|
356
|
+
return Int64(fd) + 1
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/** 最近一次失败的明细(包级共享槽;见文件头与桌面线程局部语义的差异说明) */
|
|
360
|
+
@When[env == "ohos"]
|
|
361
|
+
public func serialLastError(): String {
|
|
362
|
+
OHOS_SERIAL_ERR_MUTEX.lock()
|
|
363
|
+
let msg = ohosSerialLastError
|
|
364
|
+
OHOS_SERIAL_ERR_MUTEX.unlock()
|
|
365
|
+
return msg
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/** ohos 不走桌面 C 桥的枚举(返回 None,list 命令走下面的 serialPortList 目录扫描) */
|
|
369
|
+
@When[env == "ohos"]
|
|
370
|
+
public func serialListC(): Option<ArrayList<String>> {
|
|
371
|
+
return None
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* 读:最多 maxBytes 字节、最多等 timeoutMs 毫秒(0 = 立即返回)。
|
|
376
|
+
* 返回实际读到的字节(空数组 = 超时无数据)。poll 有数据才 read 一次——
|
|
377
|
+
* 与桌面 Linux 桥「poll + read」同口径,不会在没数据时白等。
|
|
378
|
+
*/
|
|
379
|
+
@When[env == "ohos"]
|
|
380
|
+
public func serialRead(h: Int64, maxBytes: Int32, timeoutMs: Int32): Array<UInt8> {
|
|
381
|
+
if (usbSerialIsHandle(h)) {
|
|
382
|
+
return usbSerialReadHandle(h, maxBytes, timeoutMs)
|
|
383
|
+
}
|
|
384
|
+
let fd = Int32(h - 1)
|
|
385
|
+
let ready = serialOhosPollOne(fd, OHOS_POLLIN, timeoutMs)
|
|
386
|
+
if (ready <= 0) {
|
|
387
|
+
if (ready < 0) {
|
|
388
|
+
serialOhosSetError("poll failed: ${serialOhosErrnoText()}")
|
|
389
|
+
}
|
|
390
|
+
return Array<UInt8>(0, repeat: 0)
|
|
391
|
+
}
|
|
392
|
+
let cap = Int64(maxBytes)
|
|
393
|
+
let buf = LibC.malloc<UInt8>(count: cap)
|
|
394
|
+
var n: Int64 = 0
|
|
395
|
+
unsafe {
|
|
396
|
+
n = read(fd, buf, UInt64(cap))
|
|
397
|
+
}
|
|
398
|
+
var out = Array<UInt8>(0, repeat: 0)
|
|
399
|
+
if (n > 0) {
|
|
400
|
+
out = Array<UInt8>(n, repeat: 0)
|
|
401
|
+
unsafe {
|
|
402
|
+
for (i in 0..n) {
|
|
403
|
+
out[i] = buf.read(i)
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
} else if (n < 0) {
|
|
407
|
+
serialOhosSetError("read failed: ${serialOhosErrnoText()}")
|
|
408
|
+
}
|
|
409
|
+
unsafe {
|
|
410
|
+
LibC.free(buf)
|
|
411
|
+
}
|
|
412
|
+
return out
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* 写:在 timeoutMs 预算内尽量写完,返回实际写入字节数(可能 < data.size;负码 = 失败)。
|
|
417
|
+
* EAGAIN 时 poll(POLLOUT) 等剩余预算,与桌面「预算内尽量写完」的口径一致。
|
|
418
|
+
*/
|
|
419
|
+
@When[env == "ohos"]
|
|
420
|
+
public func serialWrite(h: Int64, data: Array<UInt8>, timeoutMs: Int32): Int32 {
|
|
421
|
+
if (data.size == 0) {
|
|
422
|
+
return 0
|
|
423
|
+
}
|
|
424
|
+
if (usbSerialIsHandle(h)) {
|
|
425
|
+
return usbSerialWriteHandle(h, data, timeoutMs)
|
|
426
|
+
}
|
|
427
|
+
let fd = Int32(h - 1)
|
|
428
|
+
let n = data.size
|
|
429
|
+
let buf = LibC.malloc<UInt8>(count: Int64(n))
|
|
430
|
+
unsafe {
|
|
431
|
+
for (i in 0..n) {
|
|
432
|
+
buf.write(i, data[i])
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
let deadline = MonoTime.now() + Duration.millisecond * Int64(timeoutMs)
|
|
436
|
+
var total: Int64 = 0
|
|
437
|
+
while (total < n) {
|
|
438
|
+
var w: Int64 = 0
|
|
439
|
+
unsafe {
|
|
440
|
+
w = write(fd, buf + total, UInt64(n - total))
|
|
441
|
+
}
|
|
442
|
+
if (w > 0) {
|
|
443
|
+
total += w
|
|
444
|
+
continue
|
|
445
|
+
}
|
|
446
|
+
if (w < 0 && serialOhosErrno() != OHOS_EAGAIN) {
|
|
447
|
+
serialOhosSetError("write failed: ${serialOhosErrnoText()}")
|
|
448
|
+
unsafe {
|
|
449
|
+
LibC.free(buf)
|
|
450
|
+
}
|
|
451
|
+
return -1
|
|
452
|
+
}
|
|
453
|
+
let remainMs = (deadline - MonoTime.now()).toMilliseconds()
|
|
454
|
+
if (remainMs <= 0) {
|
|
455
|
+
break
|
|
456
|
+
}
|
|
457
|
+
let pr = serialOhosPollOne(fd, OHOS_POLLOUT, Int32(remainMs))
|
|
458
|
+
if (pr < 0) {
|
|
459
|
+
serialOhosSetError("poll failed: ${serialOhosErrnoText()}")
|
|
460
|
+
unsafe {
|
|
461
|
+
LibC.free(buf)
|
|
462
|
+
}
|
|
463
|
+
return -1
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
unsafe {
|
|
467
|
+
LibC.free(buf)
|
|
468
|
+
}
|
|
469
|
+
return Int32(total)
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/** 关闭:0 = 成功;fd 已关(EBADF)按桌面口径返回 -4(幂等拒绝重复关) */
|
|
473
|
+
@When[env == "ohos"]
|
|
474
|
+
public func serialClose(h: Int64): Int32 {
|
|
475
|
+
if (usbSerialIsHandle(h)) {
|
|
476
|
+
return usbSerialCloseHandle(h)
|
|
477
|
+
}
|
|
478
|
+
let fd = Int32(h - 1)
|
|
479
|
+
let rc = unsafe {
|
|
480
|
+
close(fd)
|
|
481
|
+
}
|
|
482
|
+
if (rc == 0) {
|
|
483
|
+
return 0
|
|
484
|
+
}
|
|
485
|
+
serialOhosSetError("close failed: ${serialOhosErrnoText()}")
|
|
486
|
+
return -4
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* 枚举本机串口(ohos 版):扫 /dev 顶层 + /dev/bus(鸿蒙特有的 USB 串口落点,sscom 同款),
|
|
491
|
+
* 按前缀过滤。拿不到目录就跳过;没有设备回空数组——空数组在页面侧语义就是「未发现端口」。
|
|
492
|
+
*/
|
|
493
|
+
@When[env == "ohos"]
|
|
494
|
+
public func serialPortList(): ArrayList<String> {
|
|
495
|
+
let prefixes = ["ttyUSB", "ttyACM", "ttyS", "ttyAMA", "ttyGS", "ttySC", "ttyPS", "ttyTHS", "tty.", "cu."]
|
|
496
|
+
let found = ArrayList<String>()
|
|
497
|
+
for (dir in ["/dev", "/dev/bus"]) {
|
|
498
|
+
if (!exists(dir)) {
|
|
499
|
+
continue
|
|
500
|
+
}
|
|
501
|
+
var entries: Array<FileInfo>
|
|
502
|
+
try {
|
|
503
|
+
entries = Directory.readFrom(dir)
|
|
504
|
+
} catch (e: Exception) {
|
|
505
|
+
continue
|
|
506
|
+
}
|
|
507
|
+
for (info in entries) {
|
|
508
|
+
if (info.isDirectory()) {
|
|
509
|
+
continue
|
|
510
|
+
}
|
|
511
|
+
let name = info.path.toString()
|
|
512
|
+
for (p in prefixes) {
|
|
513
|
+
if (name.contains(p)) {
|
|
514
|
+
found.add(name)
|
|
515
|
+
break
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
// USB 串口只有 serialManager 认得(设备节点由系统代管,/dev 扫描看不见);
|
|
521
|
+
// 有权限时两边可能都出现同一个名字,去重后再排序
|
|
522
|
+
for (name in usbSerialListNames()) {
|
|
523
|
+
if (!found.contains(name)) {
|
|
524
|
+
found.add(name)
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
sort(found)
|
|
528
|
+
return found
|
|
529
|
+
}
|
|
@@ -3,7 +3,11 @@ package cjTauri.plugins.shell
|
|
|
3
3
|
import cjTauri.*
|
|
4
4
|
|
|
5
5
|
import std.collection.*
|
|
6
|
+
// std.fs / std.process 只被桌面两端实现用到(ohos 桩不引用,RFC §4.6.4 P-1);
|
|
7
|
+
// 将来加新目标(iOS/Android)时按 P-1 重新评估门禁
|
|
8
|
+
@When[env != "ohos"]
|
|
6
9
|
import std.fs.*
|
|
10
|
+
@When[env != "ohos"]
|
|
7
11
|
import std.process.*
|
|
8
12
|
import stdx.encoding.json.*
|
|
9
13
|
|
|
@@ -26,6 +30,14 @@ import stdx.encoding.json.*
|
|
|
26
30
|
* (编辑器、REPL、npm run dev)仍不要走这里。
|
|
27
31
|
*/
|
|
28
32
|
public class ShellPlugin <: Plugin {
|
|
33
|
+
|
|
34
|
+
/** 自描述:进程/打开目标仅桌面两端实现(ohos 侧按 P-1 落桩,批次 3-PR3) */
|
|
35
|
+
public func describe(): PluginManifest {
|
|
36
|
+
let hosts = ArrayList<String>()
|
|
37
|
+
hosts.add("webkitgtk")
|
|
38
|
+
hosts.add("webview2")
|
|
39
|
+
return PluginManifest(hosts, ArrayList<String>())
|
|
40
|
+
}
|
|
29
41
|
public init() {}
|
|
30
42
|
|
|
31
43
|
public func name(): String {
|
|
@@ -106,6 +118,7 @@ public func decodeCaptured(bytes: Array<Byte>): String {
|
|
|
106
118
|
* 跑子进程并返回 (退出码, stdout, stderr)。
|
|
107
119
|
* 程序不存在 / 没有执行权限等情况统一转成 CommandException,前端拿到的是可读原文。
|
|
108
120
|
*/
|
|
121
|
+
@When[env != "ohos"]
|
|
109
122
|
func runCaptured(program: String, argv: Array<String>, cwd: String): (Int64, Array<Byte>, Array<Byte>) {
|
|
110
123
|
try {
|
|
111
124
|
if (cwd.size == 0) {
|
|
@@ -126,7 +139,8 @@ func runCaptured(program: String, argv: Array<String>, cwd: String): (Int64, Arr
|
|
|
126
139
|
* 且**不必**经过 `cmd /c start`,省掉一层引号解析与注入面)。
|
|
127
140
|
* 只启动、不等待:用户关不关那个程序与本应用无关,所以用 launch 而不是 execute(后者会阻塞 UI 线程)。
|
|
128
141
|
*/
|
|
129
|
-
|
|
142
|
+
// 鸿蒙目标下 os 仍报 Linux(探针实测),这段必须显式排除 ohos——否则会与下面的 ohos 桩重名
|
|
143
|
+
@When[os == "Linux" && env != "ohos"]
|
|
130
144
|
func launchOpenTarget(target: String): Unit {
|
|
131
145
|
let _ = launch("xdg-open", [target])
|
|
132
146
|
}
|
|
@@ -136,6 +150,20 @@ func launchOpenTarget(target: String): Unit {
|
|
|
136
150
|
let _ = launch("rundll32.exe", ["url.dll,FileProtocolHandler", target])
|
|
137
151
|
}
|
|
138
152
|
|
|
153
|
+
// ---------- ohos 桩(RFC §4.6.4 P-1):编得过、运行期可照做地报错 ----------
|
|
154
|
+
// 平台实现分段的既有先例(launchOpenTarget 的 Linux/Windows 两段);鸿蒙的原生进程/打开
|
|
155
|
+
// 通路批次 4 走 NDK FFI 接通后,删掉这两段桩、补上真实实现段。
|
|
156
|
+
|
|
157
|
+
@When[env == "ohos"]
|
|
158
|
+
func launchOpenTarget(target: String): Unit {
|
|
159
|
+
throw CommandException("shell: shell:open 在 ohos 端暂不支持(P-1 桩,批次 4 走 NDK FFI 接通,见 RFC §4.6.4)")
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
@When[env == "ohos"]
|
|
163
|
+
func runCaptured(program: String, argv: Array<String>, cwd: String): (Int64, Array<Byte>, Array<Byte>) {
|
|
164
|
+
throw CommandException("shell: shell:exec 在 ohos 端暂不支持(P-1 桩,批次 4 走 NDK FFI 接通,见 RFC §4.6.4)")
|
|
165
|
+
}
|
|
166
|
+
|
|
139
167
|
/**
|
|
140
168
|
* shell 插件的命令处理器(两个命令共用一份,按全名分派,与内置 SystemCommands 同形)
|
|
141
169
|
*/
|
|
@@ -52,9 +52,17 @@ let SQLITE_ENV_FIXTURE: String = "CJ_SQLITE_FIXTURE"
|
|
|
52
52
|
/**
|
|
53
53
|
* `sqlite` 插件:只声明「我提供什么」,放行由应用的 capabilities 决定(RFC §5.3)
|
|
54
54
|
*/
|
|
55
|
+
// 桌面实现;ohos 侧的同名桩在 sqlite_ohos_stub.cj(RFC §4.6.4 P-1:编得过、调用即可照做地报错)
|
|
55
56
|
public class SqlitePlugin <: Plugin {
|
|
56
57
|
public init() {}
|
|
57
58
|
|
|
59
|
+
/** 自描述:FFI 直连系统 sqlite3,仅 Linux 桌面验证过(Windows 未落地、ohos 未验证) */
|
|
60
|
+
public func describe(): PluginManifest {
|
|
61
|
+
let hosts = ArrayList<String>()
|
|
62
|
+
hosts.add("webkitgtk")
|
|
63
|
+
return PluginManifest(hosts, ArrayList<String>())
|
|
64
|
+
}
|
|
65
|
+
|
|
58
66
|
public func name(): String {
|
|
59
67
|
return "sqlite"
|
|
60
68
|
}
|