From 3cd8c77a0b629eba93c3da3030c4e41c099142b7 Mon Sep 17 00:00:00 2001 From: qiin2333 <414382190@qq.com> Date: Thu, 2 Apr 2026 14:31:37 +0800 Subject: [PATCH 1/9] =?UTF-8?q?feat:=20=E6=84=9A=E4=BA=BA=E8=8A=82?= =?UTF-8?q?=E5=BD=A9=E8=9B=8B=20-=20=E8=B4=9F=E5=BB=B6=E8=BF=9F=E4=B8=B2?= =?UTF-8?q?=E6=B5=81=E2=84=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 4月1日当天性能面板所有延迟值显示为负数 - 解码延迟图标变为 ❄️,颜色变为青色 - 首次显示弹出 toast: 负延迟串流™ 已激活 - 非愚人节完全无影响 --- .../ets/components/PerformanceOverlay.ets | 54 ++++++++++++++----- .../components/PerformanceOverlayManager.ets | 9 ++++ 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/entry/src/main/ets/components/PerformanceOverlay.ets b/entry/src/main/ets/components/PerformanceOverlay.ets index fa66a9d..9ddd923 100644 --- a/entry/src/main/ets/components/PerformanceOverlay.ets +++ b/entry/src/main/ets/components/PerformanceOverlay.ets @@ -22,7 +22,8 @@ import { getMoonPhaseIcon, formatDataRate, getDecoderString, getPacketLossColor, getLatencyColor, getBatteryColor, calculateSnapPosition, findNearestSnapPosition, - getPerformanceItemInfo, getSnapBorderRadius, SnapPosition + getPerformanceItemInfo, getSnapBorderRadius, SnapPosition, + isAprilFools } from './PerformanceOverlayManager'; import { StreamViewModel } from '../viewmodel/StreamViewModel'; import { PreferencesUtil } from '../utils/PreferencesUtil'; @@ -60,6 +61,7 @@ export struct PerformanceOverlay { @State perfBatteryLevel: number = 100; @State perfIsCharging: boolean = false; @State perfUpscaleMode: number = 0; + private aprilFoolsToastShown: boolean = false; // 覆盖层状态 @State overlayX: number = 8; @@ -191,6 +193,26 @@ export struct PerformanceOverlay { this.perfHostLatency = stats.hostLatency; this.perfPacketLoss = stats.packetLoss; + // 🎃 愚人节彩蛋:负延迟串流™ + if (isAprilFools()) { + if (stats.latency > 0) { + this.perfLatency = -(stats.latency * (0.3 + Math.random() * 0.4)); + } + if (stats.networkLatency > 0) { + this.perfNetworkLatency = -Math.ceil(stats.networkLatency * (0.2 + Math.random() * 0.3)); + } + if (stats.hostLatency > 0) { + this.perfHostLatency = -(stats.hostLatency * (0.3 + Math.random() * 0.3)); + } + if (!this.aprilFoolsToastShown) { + this.aprilFoolsToastShown = true; + promptAction.showToast({ + message: '🔮 负延迟串流™ 已激活\n画面将在你操作之前到达', + duration: 5000 + }); + } + } + // 延迟查询超分引擎(解码器初始化后才有值) if (this.perfUpscaleMode === 0) { const mode = session.getActiveUpscaleMode(); @@ -558,11 +580,12 @@ export struct PerformanceOverlay { // 网络延迟行 if (this.enabledItemsList.includes(PerformanceItem.NETWORK_LATENCY)) { Row() { - this.PerfIcon(PerfSymbols.NETWORK, 14, this.perfNetworkLatency > 50 ? '#FFA726' : '#4CAF50') - Text(this.perfNetworkLatency > 0 ? ` ${this.perfNetworkLatency}` : ' N/A') + this.PerfIcon(PerfSymbols.NETWORK, 14, + this.perfNetworkLatency < 0 ? '#64FFDA' : (this.perfNetworkLatency > 50 ? '#FFA726' : '#4CAF50')) + Text(this.perfNetworkLatency !== 0 ? ` ${this.perfNetworkLatency}` : ' N/A') .fontSize(12) .fontWeight(FontWeight.Medium) - .fontColor(this.perfNetworkLatency > 50 ? '#FFA726' : '#4CAF50') + .fontColor(this.perfNetworkLatency < 0 ? '#64FFDA' : (this.perfNetworkLatency > 50 ? '#FFA726' : '#4CAF50')) Text(' ms') .fontSize(11) .fontColor('#CCFFFFFF') @@ -575,6 +598,8 @@ export struct PerformanceOverlay { Row() { if (this.perfLatency >= 20) { Text('🔥').fontSize(13) + } else if (this.perfLatency < 0) { + Text('❄️').fontSize(13) } else { this.PerfIcon(PerfSymbols.DECODE_NORMAL, 14, getLatencyColor(this.perfLatency)) } @@ -592,11 +617,11 @@ export struct PerformanceOverlay { // 主机延迟行 if (this.enabledItemsList.includes(PerformanceItem.HOST_LATENCY)) { Row() { - this.PerfIcon(PerfSymbols.HOST, 14, '#009688') - Text(this.perfHostLatency > 0 ? ` ${this.perfHostLatency.toFixed(1)}` : ' N/A') + this.PerfIcon(PerfSymbols.HOST, 14, this.perfHostLatency < 0 ? '#64FFDA' : '#009688') + Text(this.perfHostLatency !== 0 ? ` ${this.perfHostLatency.toFixed(1)}` : ' N/A') .fontSize(12) .fontWeight(FontWeight.Medium) - .fontColor('#009688') + .fontColor(this.perfHostLatency < 0 ? '#64FFDA' : '#009688') Text(' ms') .fontSize(11) .fontColor('#CCFFFFFF') @@ -689,11 +714,12 @@ export struct PerformanceOverlay { // 网络延迟 if (this.enabledItemsList.includes(PerformanceItem.NETWORK_LATENCY)) { - this.PerfIcon(PerfSymbols.NETWORK, 13, this.perfNetworkLatency > 50 ? '#FFA726' : '#4CAF50') - Text(this.perfNetworkLatency > 0 ? ` ${this.perfNetworkLatency} ms` : ' N/A') + this.PerfIcon(PerfSymbols.NETWORK, 13, + this.perfNetworkLatency < 0 ? '#64FFDA' : (this.perfNetworkLatency > 50 ? '#FFA726' : '#4CAF50')) + Text(this.perfNetworkLatency !== 0 ? ` ${this.perfNetworkLatency} ms` : ' N/A') .fontSize(11) .fontWeight(FontWeight.Medium) - .fontColor(this.perfNetworkLatency > 50 ? '#FFA726' : '#4CAF50') + .fontColor(this.perfNetworkLatency < 0 ? '#64FFDA' : (this.perfNetworkLatency > 50 ? '#FFA726' : '#4CAF50')) if (this.hasNextItem(PerformanceItem.NETWORK_LATENCY)) this.Separator() } @@ -701,6 +727,8 @@ export struct PerformanceOverlay { if (this.enabledItemsList.includes(PerformanceItem.DECODE_LATENCY)) { if (this.perfLatency >= 20) { Text('🥵').fontSize(12) + } else if (this.perfLatency < 0) { + Text('❄️').fontSize(12) } else { this.PerfIcon(PerfSymbols.DECODE_NORMAL, 13, getLatencyColor(this.perfLatency)) } @@ -713,11 +741,11 @@ export struct PerformanceOverlay { // 主机延迟 if (this.enabledItemsList.includes(PerformanceItem.HOST_LATENCY)) { - this.PerfIcon(PerfSymbols.HOST, 13, '#009688') - Text(this.perfHostLatency > 0 ? ` ${this.perfHostLatency.toFixed(1)} ms` : ' N/A') + this.PerfIcon(PerfSymbols.HOST, 13, this.perfHostLatency < 0 ? '#64FFDA' : '#009688') + Text(this.perfHostLatency !== 0 ? ` ${this.perfHostLatency.toFixed(1)} ms` : ' N/A') .fontSize(11) .fontWeight(FontWeight.Medium) - .fontColor('#009688') + .fontColor(this.perfHostLatency < 0 ? '#64FFDA' : '#009688') if (this.hasNextItem(PerformanceItem.HOST_LATENCY)) this.Separator() } diff --git a/entry/src/main/ets/components/PerformanceOverlayManager.ets b/entry/src/main/ets/components/PerformanceOverlayManager.ets index 8b4f6ad..e2b9a00 100644 --- a/entry/src/main/ets/components/PerformanceOverlayManager.ets +++ b/entry/src/main/ets/components/PerformanceOverlayManager.ets @@ -270,11 +270,20 @@ export function getPacketLossColor(loss: number): string { * 获取延迟颜色 */ export function getLatencyColor(ms: number): string { + if (ms < 0) return '#64FFDA'; // 青色 - 负延迟™ if (ms < 10) return '#7D9D7D'; // 绿色 - 优秀 if (ms < 20) return '#D597E3'; // 紫色 - 正常 return '#B57D7D'; // 红色 - 差 } +/** + * 愚人节彩蛋:负延迟串流™ + */ +export function isAprilFools(): boolean { + const now = new Date(); + return now.getMonth() === 3 && now.getDate() === 1; +} + /** * 获取电池颜色 */ From 69286840b857d898003ef6d93b1f8644ed9cb5f3 Mon Sep 17 00:00:00 2001 From: qiin2333 <414382190@qq.com> Date: Thu, 2 Apr 2026 14:31:43 +0800 Subject: [PATCH 2/9] =?UTF-8?q?fix:=20=E8=AE=BE=E7=BD=AE=E9=A1=B5=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=8F=B7=E7=82=B9=E5=87=BB=E5=A4=8D=E5=88=B6=20+=20?= =?UTF-8?q?=E8=83=8C=E6=99=AF=E5=9B=BE=E5=88=87=E6=8D=A2=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E9=87=8D=E5=90=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 版本号点击复制到剪贴板并 toast 反馈 - 背景图片类型切换后提示重启应用生效 --- entry/src/main/ets/pages/SettingsPageV2.ets | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/entry/src/main/ets/pages/SettingsPageV2.ets b/entry/src/main/ets/pages/SettingsPageV2.ets index 9b40ca5..226de07 100644 --- a/entry/src/main/ets/pages/SettingsPageV2.ets +++ b/entry/src/main/ets/pages/SettingsPageV2.ets @@ -2176,7 +2176,11 @@ struct SettingsPageV2 { title: '版本', subtitle: this.appVersion, type: 'action', - action: () => {} + action: () => { + const pasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, this.appVersion); + pasteboard.getSystemPasteboard().setDataSync(pasteData); + this.showToast(`已复制版本号: ${this.appVersion}`); + } }, { title: '更新日志', @@ -3590,7 +3594,7 @@ struct SettingsPageV2 { if (option.value === 'none') { bgUtil.disableBackgroundImage(); } - this.showToast(`背景图片设置为: ${option.title}`); + this.showToast(`背景图片设置为: ${option.title},重启应用后生效`); } }; this.showPicker(config); From e4fde27307bc5e48063e374e5a088b99b982c97f Mon Sep 17 00:00:00 2001 From: qiin2333 <414382190@qq.com> Date: Thu, 2 Apr 2026 14:37:29 +0800 Subject: [PATCH 3/9] =?UTF-8?q?ci:=20=E6=8E=A5=E5=85=A5=20CodeRabbit=20AI?= =?UTF-8?q?=20=E4=BB=A3=E7=A0=81=E5=AE=A1=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .coderabbit.yaml | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .coderabbit.yaml diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000..dd5fb4d --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,40 @@ +# CodeRabbit AI 代码审查配置 +# 文档: https://docs.coderabbit.ai/reference/configuration + +language: "zh-CN" +tone_instructions: "使用简洁的中文进行代码审查,关注实际问题而非风格偏好。" +early_access: true + +reviews: + profile: "chill" + high_level_summary: true + poem: false + sequence_diagrams: false + collapse_walkthrough: true + changed_files_summary: true + path_filters: + - "!build/**" + - "!oh_modules/**" + - "!entry/oh_modules/**" + - "!entry/build/**" + - "!nativelib/build/**" + - "!hvigor/**" + - "!oh-package-lock.json5" + - "!entry/oh-package-lock.json5" + - "!store-assets/**" + - "!nativelib/src/main/cpp/moonlight-common-c/**" + - "!nativelib/src/main/cpp/ohos-openssl/**" + path_instructions: + - path: "entry/src/main/ets/**" + instructions: | + 这是 HarmonyOS ArkTS 前端代码。使用 ArkUI 声明式 UI 框架,@Component/@State/@Prop/@Link 等装饰器。 + 关注线程安全(UI 线程 vs Worker)、内存泄漏(定时器/事件监听未清理)、空指针安全。 + - path: "nativelib/src/main/cpp/**" + instructions: | + C/C++ 原生层代码,通过 N-API 与 ArkTS 交互。关注内存管理、线程安全、JNI/N-API 生命周期。 + auto_review: + enabled: true + drafts: false + +chat: + auto_reply: true From ca07c9f74f6516f96d71c6b292f74bee2a38e488 Mon Sep 17 00:00:00 2001 From: qiin2333 <414382190@qq.com> Date: Thu, 2 Apr 2026 16:22:35 +0800 Subject: [PATCH 4/9] =?UTF-8?q?fix(ci):=20CI=20=E7=BC=96=E8=AF=91=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E5=90=88=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GCK helper 函数移入 #if GAME_CONTROLLER_KIT_AVAILABLE 条件编译块 - ScanKit/ShareKit SDK stubs (kit_configs + ohos 模块 + declare module) - DevKeySecret 自动生成 - catch(e) 类型修复: JSON.stringify(e) → String(e) - VirtualKeyboard 回调移除 void 返回类型注解 - VirtualKeyboard 包裹 Column 容器(恢复旧写法) --- ci/patch-sdk.sh | 23 +++++++++++++ ci/sdk-stubs/kit.ScanKit.d.ts | 22 +++++++++++++ ci/sdk-stubs/kit.ScanKit.json | 12 +++++++ ci/sdk-stubs/kit.ShareKit.d.ts | 33 +++++++++++++++++++ ci/sdk-stubs/kit.ShareKit.json | 8 +++++ ci/sdk-stubs/ohos.scan.scanBarcode.d.ts | 13 ++++++++ ci/sdk-stubs/ohos.scan.scanCore.d.ts | 9 +++++ ci/sdk-stubs/ohos.share.systemShare.d.ts | 32 ++++++++++++++++++ .../main/ets/components/CustomKeyOverlay.ets | 2 +- entry/src/main/ets/pages/StreamPage.ets | 26 ++++++++------- entry/src/main/ets/service/QrShareService.ets | 2 +- .../src/main/cpp/game_controller_native.cpp | 2 ++ 12 files changed, 170 insertions(+), 14 deletions(-) create mode 100644 ci/sdk-stubs/kit.ScanKit.d.ts create mode 100644 ci/sdk-stubs/kit.ScanKit.json create mode 100644 ci/sdk-stubs/kit.ShareKit.d.ts create mode 100644 ci/sdk-stubs/kit.ShareKit.json create mode 100644 ci/sdk-stubs/ohos.scan.scanBarcode.d.ts create mode 100644 ci/sdk-stubs/ohos.scan.scanCore.d.ts create mode 100644 ci/sdk-stubs/ohos.share.systemShare.d.ts diff --git a/ci/patch-sdk.sh b/ci/patch-sdk.sh index 09fa7c0..879da28 100644 --- a/ci/patch-sdk.sh +++ b/ci/patch-sdk.sh @@ -116,4 +116,27 @@ if [ -n "$DISPLAY_DTS" ] && ! grep -q "getBrightnessInfo" "$DISPLAY_DTS"; then echo " Patched display module" fi +# ─── @kit.ScanKit ─── +[ ! -f "$KIT_CONFIGS/@kit.ScanKit.json" ] && \ + cp "$STUBS_DIR/kit.ScanKit.json" "$KIT_CONFIGS/@kit.ScanKit.json" +cp "$STUBS_DIR/kit.ScanKit.d.ts" "$ETS_API/@kit.ScanKit.d.ts" +cp "$STUBS_DIR/ohos.scan.scanCore.d.ts" "$ETS_API/@ohos.scan.scanCore.d.ts" +cp "$STUBS_DIR/ohos.scan.scanBarcode.d.ts" "$ETS_API/@ohos.scan.scanBarcode.d.ts" +echo " Applied ScanKit stubs" + +# ─── @kit.ShareKit ─── +[ ! -f "$KIT_CONFIGS/@kit.ShareKit.json" ] && \ + cp "$STUBS_DIR/kit.ShareKit.json" "$KIT_CONFIGS/@kit.ShareKit.json" +cp "$STUBS_DIR/kit.ShareKit.d.ts" "$ETS_API/@kit.ShareKit.d.ts" +cp "$STUBS_DIR/ohos.share.systemShare.d.ts" "$ETS_API/@ohos.share.systemShare.d.ts" +echo " Applied ShareKit stubs" + +# ─── DevKeySecret (CI-only placeholder) ─── +DEV_KEY_SECRET="entry/src/main/ets/config/DevKeySecret.ets" +if [ ! -f "$DEV_KEY_SECRET" ]; then + mkdir -p "$(dirname "$DEV_KEY_SECRET")" + cp "${DEV_KEY_SECRET}.example" "$DEV_KEY_SECRET" + echo " Created DevKeySecret from example" +fi + echo "✅ SDK patches applied" diff --git a/ci/sdk-stubs/kit.ScanKit.d.ts b/ci/sdk-stubs/kit.ScanKit.d.ts new file mode 100644 index 0000000..3ae5b5d --- /dev/null +++ b/ci/sdk-stubs/kit.ScanKit.d.ts @@ -0,0 +1,22 @@ +declare module '@kit.ScanKit' { + namespace scanCore { + enum ScanType { + QR_CODE = 0, + BARCODE_TYPE_EAN_13 = 1, + BARCODE_TYPE_EAN_8 = 2, + BARCODE_TYPE_CODE_128 = 3, + } + } + namespace scanBarcode { + interface ScanOptions { + scanTypes?: scanCore.ScanType[]; + enableMultiMode?: boolean; + enableAlbum?: boolean; + } + interface ScanResult { + originalValue?: string; + scanType?: number; + } + function startScanForResult(context: object, options?: ScanOptions): Promise; + } +} diff --git a/ci/sdk-stubs/kit.ScanKit.json b/ci/sdk-stubs/kit.ScanKit.json new file mode 100644 index 0000000..cc519f4 --- /dev/null +++ b/ci/sdk-stubs/kit.ScanKit.json @@ -0,0 +1,12 @@ +{ + "symbols": { + "scanCore": { + "source": "@ohos.scan.scanCore.d.ts", + "bindings": "default" + }, + "scanBarcode": { + "source": "@ohos.scan.scanBarcode.d.ts", + "bindings": "default" + } + } +} diff --git a/ci/sdk-stubs/kit.ShareKit.d.ts b/ci/sdk-stubs/kit.ShareKit.d.ts new file mode 100644 index 0000000..036ea0b --- /dev/null +++ b/ci/sdk-stubs/kit.ShareKit.d.ts @@ -0,0 +1,33 @@ +declare module '@kit.ShareKit' { + namespace systemShare { + enum SelectionMode { + SINGLE = 0, + BATCH = 1, + } + enum SharePreviewMode { + DEFAULT = 0, + DETAIL = 1, + } + interface SharedDataRecord { + utd: string; + content?: string; + uri?: string; + data?: ArrayBuffer; + title?: string; + description?: string; + thumbnail?: object; + } + class SharedData { + constructor(record: SharedDataRecord); + addRecord(record: SharedDataRecord): void; + } + interface ShareControllerOptions { + selectionMode?: SelectionMode; + previewMode?: SharePreviewMode; + } + class ShareController { + constructor(data: SharedData); + show(context: object, options?: ShareControllerOptions): Promise; + } + } +} diff --git a/ci/sdk-stubs/kit.ShareKit.json b/ci/sdk-stubs/kit.ShareKit.json new file mode 100644 index 0000000..de7fab4 --- /dev/null +++ b/ci/sdk-stubs/kit.ShareKit.json @@ -0,0 +1,8 @@ +{ + "symbols": { + "systemShare": { + "source": "@ohos.share.systemShare.d.ts", + "bindings": "default" + } + } +} diff --git a/ci/sdk-stubs/ohos.scan.scanBarcode.d.ts b/ci/sdk-stubs/ohos.scan.scanBarcode.d.ts new file mode 100644 index 0000000..dedd158 --- /dev/null +++ b/ci/sdk-stubs/ohos.scan.scanBarcode.d.ts @@ -0,0 +1,13 @@ +declare namespace scanBarcode { + interface ScanOptions { + scanTypes?: number[]; + enableMultiMode?: boolean; + enableAlbum?: boolean; + } + interface ScanResult { + originalValue?: string; + scanType?: number; + } + function startScanForResult(context: object, options?: ScanOptions): Promise; +} +export default scanBarcode; diff --git a/ci/sdk-stubs/ohos.scan.scanCore.d.ts b/ci/sdk-stubs/ohos.scan.scanCore.d.ts new file mode 100644 index 0000000..59f370e --- /dev/null +++ b/ci/sdk-stubs/ohos.scan.scanCore.d.ts @@ -0,0 +1,9 @@ +declare namespace scanCore { + enum ScanType { + QR_CODE = 0, + BARCODE_TYPE_EAN_13 = 1, + BARCODE_TYPE_EAN_8 = 2, + BARCODE_TYPE_CODE_128 = 3, + } +} +export default scanCore; diff --git a/ci/sdk-stubs/ohos.share.systemShare.d.ts b/ci/sdk-stubs/ohos.share.systemShare.d.ts new file mode 100644 index 0000000..b03265c --- /dev/null +++ b/ci/sdk-stubs/ohos.share.systemShare.d.ts @@ -0,0 +1,32 @@ +declare namespace systemShare { + enum SelectionMode { + SINGLE = 0, + BATCH = 1, + } + enum SharePreviewMode { + DEFAULT = 0, + DETAIL = 1, + } + interface SharedDataRecord { + utd: string; + content?: string; + uri?: string; + data?: ArrayBuffer; + title?: string; + description?: string; + thumbnail?: object; + } + class SharedData { + constructor(record: SharedDataRecord); + addRecord(record: SharedDataRecord): void; + } + interface ShareControllerOptions { + selectionMode?: SelectionMode; + previewMode?: SharePreviewMode; + } + class ShareController { + constructor(data: SharedData); + show(context: object, options?: ShareControllerOptions): Promise; + } +} +export default systemShare; diff --git a/entry/src/main/ets/components/CustomKeyOverlay.ets b/entry/src/main/ets/components/CustomKeyOverlay.ets index 5c76b7e..5a615c1 100644 --- a/entry/src/main/ets/components/CustomKeyOverlay.ets +++ b/entry/src/main/ets/components/CustomKeyOverlay.ets @@ -1613,7 +1613,7 @@ export struct CustomKeyOverlay { previewMode: systemShare.SharePreviewMode.DEFAULT, }); } catch (e) { - console.error('[CustomKeyOverlay] 分享失败:', JSON.stringify(e)); + console.error('[CustomKeyOverlay] 分享失败:', String(e)); promptAction.showToast({ message: '分享失败', duration: 1500 }); } } diff --git a/entry/src/main/ets/pages/StreamPage.ets b/entry/src/main/ets/pages/StreamPage.ets index 5347d9e..aa41c44 100644 --- a/entry/src/main/ets/pages/StreamPage.ets +++ b/entry/src/main/ets/pages/StreamPage.ets @@ -1479,18 +1479,20 @@ struct StreamPage { // PC 虚拟键盘 if (this.showVirtualKeyboard && this.viewModel.isConnected) { - VirtualKeyboard({ - onVirtualKeyEvent: (vk: number, isDown: boolean): void => { - const session = this.viewModel.getSession(); - if (session) { - session.sendKeyboardInput(vk, isDown ? 0x03 : 0x04); - } - }, - onClose: (): void => { - this.showVirtualKeyboard = false; - }, - }) - .width('100%') + Column() { + VirtualKeyboard({ + onVirtualKeyEvent: (vk: number, isDown: boolean) => { + const session = this.viewModel.getSession(); + if (session) { + session.sendKeyboardInput(vk, isDown ? 0x03 : 0x04); + } + }, + onClose: () => { + this.showVirtualKeyboard = false; + }, + }) + } + .position({ left: 0, bottom: 0 }) } } diff --git a/entry/src/main/ets/service/QrShareService.ets b/entry/src/main/ets/service/QrShareService.ets index 534dffd..a903a5d 100644 --- a/entry/src/main/ets/service/QrShareService.ets +++ b/entry/src/main/ets/service/QrShareService.ets @@ -464,7 +464,7 @@ export class QrShareService { } return null; } catch (e) { - console.error('[QrShareService] 扫码失败:', JSON.stringify(e)); + console.error('[QrShareService] 扫码失败:', String(e)); return null; } } diff --git a/nativelib/src/main/cpp/game_controller_native.cpp b/nativelib/src/main/cpp/game_controller_native.cpp index fd4b50a..d3513de 100644 --- a/nativelib/src/main/cpp/game_controller_native.cpp +++ b/nativelib/src/main/cpp/game_controller_native.cpp @@ -770,6 +770,7 @@ static void OnRightTriggerAxis(const struct GamePad_AxisEvent* axisEvent) { // 按键 + 轴监听的注册/注销在 StartMonitor、StopMonitor、Pause、Resume 中重复出现 4 次 // 提取为内部 helper 消除重复 +#if GAME_CONTROLLER_KIT_AVAILABLE /** * 注册所有按键 + 轴输入监听(不含设备监听) * 调用者需持有 g_mutex 或保证线程安全 @@ -833,6 +834,7 @@ static void UnregisterAllInputMonitors() { OH_GamePad_LeftTrigger_UnregisterAxisInputMonitor(); OH_GamePad_RightTrigger_UnregisterAxisInputMonitor(); } +#endif // GAME_CONTROLLER_KIT_AVAILABLE // ==================== 公共 API ==================== From e509d43af74193f783294d72f60ef2654ee8ef0e Mon Sep 17 00:00:00 2001 From: qiin2333 <414382190@qq.com> Date: Thu, 2 Apr 2026 16:27:07 +0800 Subject: [PATCH 5/9] =?UTF-8?q?refactor(ci):=20=E5=9B=9E=E9=80=80ArkTS?= =?UTF-8?q?=E6=BA=90=E7=A0=81CI=E9=80=82=E9=85=8D=EF=BC=8Cworkflow?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E4=BB=85=E6=9E=84=E5=BB=BA=E5=8E=9F=E7=94=9F?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 回退 game_controller_native.cpp 条件编译改动 - 回退 QrShareService/CustomKeyOverlay catch 类型改动 - 回退 StreamPage VirtualKeyboard Column包裹改动 - CI workflow 改为 assembleHar 仅构建 nativelib 模块 - 移除 HAP/APP 签名构建和 Release 产物上传 --- .github/workflows/build.yml | 72 ++----------------- .../main/ets/components/CustomKeyOverlay.ets | 2 +- entry/src/main/ets/pages/StreamPage.ets | 26 ++++--- entry/src/main/ets/service/QrShareService.ets | 2 +- .../src/main/cpp/game_controller_native.cpp | 2 - 5 files changed, 18 insertions(+), 86 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6c6b49a..9afc1ce 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -301,7 +301,7 @@ jobs: ci/patch-sdk.sh ~/ohos-sdk # ── 构建 ── - - name: Build HAP + - name: Build Native run: | set -o pipefail BUILD_MODE="${{ steps.build-mode.outputs.mode }}" @@ -310,58 +310,20 @@ jobs: export JAVA_HOME="${JAVA_HOME_17_X64}" export LD_LIBRARY_PATH="${HOME}/ohos-sdk/toolchains/lib:${HOME}/ohos-sdk/previewer/common/bin:${LD_LIBRARY_PATH:-}" - node hvigorw.js assembleHap \ + node hvigorw.js assembleHar \ --mode module \ + -p module=nativelib@default \ -p product=default \ -p buildMode="${BUILD_MODE}" \ --no-daemon \ --stacktrace \ 2>&1 | tee build-hap.log - find . -name "*.hap" -type f -exec sh -c 'echo " $(du -h "$1" | cut -f1) $1"' _ {} \; - - - name: Build APP (signed) - if: steps.signing.outputs.available == 'true' - run: | - set -o pipefail - BUILD_MODE="${{ steps.build-mode.outputs.mode }}" - export NODE_HOME="$(dirname "$(dirname "$(which node)")")" - export JAVA_HOME="${JAVA_HOME_17_X64}" - - node hvigorw.js assembleApp \ - --mode module \ - -p product=default \ - -p buildMode="${BUILD_MODE}" \ - --no-daemon \ - 2>&1 | tee build-app.log - - find . -name "*.app" -type f -exec sh -c 'echo " $(du -h "$1" | cut -f1) $1"' _ {} \; - # ── 清理 & 上传 ── - name: Cleanup signing files if: always() run: rm -rf .signing/ - - name: Upload HAP artifact - uses: actions/upload-artifact@v4 - if: always() - with: - name: moonlight-hap-${{ steps.version.outputs.version }}-${{ steps.build-mode.outputs.mode }} - path: entry/build/default/outputs/**/*.hap - if-no-files-found: warn - retention-days: 30 - - - name: Upload APP artifact - uses: actions/upload-artifact@v4 - if: steps.signing.outputs.available == 'true' - with: - name: moonlight-app-${{ steps.version.outputs.version }}-${{ steps.build-mode.outputs.mode }} - path: | - entry/build/default/outputs/**/*.app - build/default/outputs/**/*.app - if-no-files-found: warn - retention-days: 30 - - name: Upload build logs on failure uses: actions/upload-artifact@v4 if: failure() @@ -369,35 +331,9 @@ jobs: name: build-logs-${{ github.run_id }} path: | build-hap.log - build-app.log retention-days: 7 - # ── Release ── - release: - name: Create Release - needs: build - runs-on: ubuntu-latest - if: startsWith(github.ref, 'refs/tags/v') - permissions: - contents: write - - steps: - - name: Download artifacts - uses: actions/download-artifact@v4 - with: - pattern: moonlight-* - path: ./artifacts - merge-multiple: true - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - files: | - ./artifacts/**/*.hap - ./artifacts/**/*.app - draft: false - prerelease: ${{ contains(github.ref, 'beta') || contains(github.ref, 'alpha') || contains(github.ref, 'rc') }} - generate_release_notes: true + # Release 由本地手动构建签名后上传 fail_on_unmatched_files: false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/entry/src/main/ets/components/CustomKeyOverlay.ets b/entry/src/main/ets/components/CustomKeyOverlay.ets index 5a615c1..5c76b7e 100644 --- a/entry/src/main/ets/components/CustomKeyOverlay.ets +++ b/entry/src/main/ets/components/CustomKeyOverlay.ets @@ -1613,7 +1613,7 @@ export struct CustomKeyOverlay { previewMode: systemShare.SharePreviewMode.DEFAULT, }); } catch (e) { - console.error('[CustomKeyOverlay] 分享失败:', String(e)); + console.error('[CustomKeyOverlay] 分享失败:', JSON.stringify(e)); promptAction.showToast({ message: '分享失败', duration: 1500 }); } } diff --git a/entry/src/main/ets/pages/StreamPage.ets b/entry/src/main/ets/pages/StreamPage.ets index aa41c44..5347d9e 100644 --- a/entry/src/main/ets/pages/StreamPage.ets +++ b/entry/src/main/ets/pages/StreamPage.ets @@ -1479,20 +1479,18 @@ struct StreamPage { // PC 虚拟键盘 if (this.showVirtualKeyboard && this.viewModel.isConnected) { - Column() { - VirtualKeyboard({ - onVirtualKeyEvent: (vk: number, isDown: boolean) => { - const session = this.viewModel.getSession(); - if (session) { - session.sendKeyboardInput(vk, isDown ? 0x03 : 0x04); - } - }, - onClose: () => { - this.showVirtualKeyboard = false; - }, - }) - } - .position({ left: 0, bottom: 0 }) + VirtualKeyboard({ + onVirtualKeyEvent: (vk: number, isDown: boolean): void => { + const session = this.viewModel.getSession(); + if (session) { + session.sendKeyboardInput(vk, isDown ? 0x03 : 0x04); + } + }, + onClose: (): void => { + this.showVirtualKeyboard = false; + }, + }) + .width('100%') } } diff --git a/entry/src/main/ets/service/QrShareService.ets b/entry/src/main/ets/service/QrShareService.ets index a903a5d..534dffd 100644 --- a/entry/src/main/ets/service/QrShareService.ets +++ b/entry/src/main/ets/service/QrShareService.ets @@ -464,7 +464,7 @@ export class QrShareService { } return null; } catch (e) { - console.error('[QrShareService] 扫码失败:', String(e)); + console.error('[QrShareService] 扫码失败:', JSON.stringify(e)); return null; } } diff --git a/nativelib/src/main/cpp/game_controller_native.cpp b/nativelib/src/main/cpp/game_controller_native.cpp index d3513de..fd4b50a 100644 --- a/nativelib/src/main/cpp/game_controller_native.cpp +++ b/nativelib/src/main/cpp/game_controller_native.cpp @@ -770,7 +770,6 @@ static void OnRightTriggerAxis(const struct GamePad_AxisEvent* axisEvent) { // 按键 + 轴监听的注册/注销在 StartMonitor、StopMonitor、Pause、Resume 中重复出现 4 次 // 提取为内部 helper 消除重复 -#if GAME_CONTROLLER_KIT_AVAILABLE /** * 注册所有按键 + 轴输入监听(不含设备监听) * 调用者需持有 g_mutex 或保证线程安全 @@ -834,7 +833,6 @@ static void UnregisterAllInputMonitors() { OH_GamePad_LeftTrigger_UnregisterAxisInputMonitor(); OH_GamePad_RightTrigger_UnregisterAxisInputMonitor(); } -#endif // GAME_CONTROLLER_KIT_AVAILABLE // ==================== 公共 API ==================== From 5c192b6ddd2b8a2b3d3282dc97ebd3d64e181a26 Mon Sep 17 00:00:00 2001 From: qiin2333 <414382190@qq.com> Date: Thu, 2 Apr 2026 16:33:08 +0800 Subject: [PATCH 6/9] =?UTF-8?q?fix:=20GCK=20helper=20=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E6=9D=A1=E4=BB=B6=E7=BC=96=E8=AF=91=EF=BC=88=E5=8E=9F=E7=94=9F?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=BF=85=E9=A1=BB=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nativelib/src/main/cpp/game_controller_native.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nativelib/src/main/cpp/game_controller_native.cpp b/nativelib/src/main/cpp/game_controller_native.cpp index fd4b50a..d3513de 100644 --- a/nativelib/src/main/cpp/game_controller_native.cpp +++ b/nativelib/src/main/cpp/game_controller_native.cpp @@ -770,6 +770,7 @@ static void OnRightTriggerAxis(const struct GamePad_AxisEvent* axisEvent) { // 按键 + 轴监听的注册/注销在 StartMonitor、StopMonitor、Pause、Resume 中重复出现 4 次 // 提取为内部 helper 消除重复 +#if GAME_CONTROLLER_KIT_AVAILABLE /** * 注册所有按键 + 轴输入监听(不含设备监听) * 调用者需持有 g_mutex 或保证线程安全 @@ -833,6 +834,7 @@ static void UnregisterAllInputMonitors() { OH_GamePad_LeftTrigger_UnregisterAxisInputMonitor(); OH_GamePad_RightTrigger_UnregisterAxisInputMonitor(); } +#endif // GAME_CONTROLLER_KIT_AVAILABLE // ==================== 公共 API ==================== From 866d057f4bc27e95d0b9c274a06dde7a67c13cbf Mon Sep 17 00:00:00 2001 From: qiin2333 <414382190@qq.com> Date: Thu, 2 Apr 2026 16:43:26 +0800 Subject: [PATCH 7/9] =?UTF-8?q?fix:=20GCK=20=E5=87=BD=E6=95=B0=E6=8C=87?= =?UTF-8?q?=E9=92=88=E7=A9=BA=E5=80=BC=E4=BF=9D=E6=8A=A4=20+=20=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RegisterAllInputMonitors/UnregisterAllInputMonitors 使用 SAFE_CALL 宏 - TryLoadGameControllerLib 增加关键符号加载验证 - 缺失符号时记录警告日志而非崩溃 --- .../src/main/cpp/game_controller_native.cpp | 115 +++++++++++------- 1 file changed, 71 insertions(+), 44 deletions(-) diff --git a/nativelib/src/main/cpp/game_controller_native.cpp b/nativelib/src/main/cpp/game_controller_native.cpp index d3513de..93e844c 100644 --- a/nativelib/src/main/cpp/game_controller_native.cpp +++ b/nativelib/src/main/cpp/game_controller_native.cpp @@ -359,6 +359,25 @@ static bool TryLoadGameControllerLib() { g_gcLibAvailable = false; return false; } + + // 验证所有按键/轴监听函数是否加载成功 + int missingCount = 0; + #define CHECK_FUNC(name) if (!pfn_##name) { LOGW("GCK: 缺少符号 " #name); missingCount++; } + CHECK_FUNC(OH_GamePad_ButtonA_RegisterButtonInputMonitor) + CHECK_FUNC(OH_GamePad_ButtonB_RegisterButtonInputMonitor) + CHECK_FUNC(OH_GamePad_ButtonX_RegisterButtonInputMonitor) + CHECK_FUNC(OH_GamePad_ButtonY_RegisterButtonInputMonitor) + CHECK_FUNC(OH_GamePad_LeftShoulder_RegisterButtonInputMonitor) + CHECK_FUNC(OH_GamePad_RightShoulder_RegisterButtonInputMonitor) + CHECK_FUNC(OH_GamePad_LeftThumbstick_RegisterAxisInputMonitor) + CHECK_FUNC(OH_GamePad_RightThumbstick_RegisterAxisInputMonitor) + CHECK_FUNC(OH_GamePad_Dpad_RegisterAxisInputMonitor) + CHECK_FUNC(OH_GamePad_LeftTrigger_RegisterAxisInputMonitor) + CHECK_FUNC(OH_GamePad_RightTrigger_RegisterAxisInputMonitor) + #undef CHECK_FUNC + if (missingCount > 0) { + LOGW("Game Controller Kit: %d 个函数符号缺失,监听可能不完整", missingCount); + } #endif LOGI("Game Controller Kit 动态库加载成功"); @@ -771,36 +790,41 @@ static void OnRightTriggerAxis(const struct GamePad_AxisEvent* axisEvent) { // 提取为内部 helper 消除重复 #if GAME_CONTROLLER_KIT_AVAILABLE + +// 安全调用宏:函数指针非空时才调用(已被 #define 重定向为 pfn_* 指针) +#define SAFE_CALL(func, ...) do { if (func) func(__VA_ARGS__); } while(0) +#define SAFE_CALL0(func) do { if (func) func(); } while(0) + /** * 注册所有按键 + 轴输入监听(不含设备监听) * 调用者需持有 g_mutex 或保证线程安全 */ static void RegisterAllInputMonitors() { // 按键监听 - OH_GamePad_ButtonA_RegisterButtonInputMonitor(OnButtonA); - OH_GamePad_ButtonB_RegisterButtonInputMonitor(OnButtonB); - OH_GamePad_ButtonX_RegisterButtonInputMonitor(OnButtonX); - OH_GamePad_ButtonY_RegisterButtonInputMonitor(OnButtonY); - OH_GamePad_ButtonC_RegisterButtonInputMonitor(OnButtonC); - OH_GamePad_LeftShoulder_RegisterButtonInputMonitor(OnLeftShoulder); - OH_GamePad_RightShoulder_RegisterButtonInputMonitor(OnRightShoulder); - OH_GamePad_LeftTrigger_RegisterButtonInputMonitor(OnLeftTriggerButton); - OH_GamePad_RightTrigger_RegisterButtonInputMonitor(OnRightTriggerButton); - OH_GamePad_LeftThumbstick_RegisterButtonInputMonitor(OnLeftThumbstick); - OH_GamePad_RightThumbstick_RegisterButtonInputMonitor(OnRightThumbstick); - OH_GamePad_ButtonHome_RegisterButtonInputMonitor(OnButtonHome); - OH_GamePad_ButtonMenu_RegisterButtonInputMonitor(OnButtonMenu); - OH_GamePad_Dpad_UpButton_RegisterButtonInputMonitor(OnDpadUp); - OH_GamePad_Dpad_DownButton_RegisterButtonInputMonitor(OnDpadDown); - OH_GamePad_Dpad_LeftButton_RegisterButtonInputMonitor(OnDpadLeft); - OH_GamePad_Dpad_RightButton_RegisterButtonInputMonitor(OnDpadRight); + SAFE_CALL(OH_GamePad_ButtonA_RegisterButtonInputMonitor, OnButtonA); + SAFE_CALL(OH_GamePad_ButtonB_RegisterButtonInputMonitor, OnButtonB); + SAFE_CALL(OH_GamePad_ButtonX_RegisterButtonInputMonitor, OnButtonX); + SAFE_CALL(OH_GamePad_ButtonY_RegisterButtonInputMonitor, OnButtonY); + SAFE_CALL(OH_GamePad_ButtonC_RegisterButtonInputMonitor, OnButtonC); + SAFE_CALL(OH_GamePad_LeftShoulder_RegisterButtonInputMonitor, OnLeftShoulder); + SAFE_CALL(OH_GamePad_RightShoulder_RegisterButtonInputMonitor, OnRightShoulder); + SAFE_CALL(OH_GamePad_LeftTrigger_RegisterButtonInputMonitor, OnLeftTriggerButton); + SAFE_CALL(OH_GamePad_RightTrigger_RegisterButtonInputMonitor, OnRightTriggerButton); + SAFE_CALL(OH_GamePad_LeftThumbstick_RegisterButtonInputMonitor, OnLeftThumbstick); + SAFE_CALL(OH_GamePad_RightThumbstick_RegisterButtonInputMonitor, OnRightThumbstick); + SAFE_CALL(OH_GamePad_ButtonHome_RegisterButtonInputMonitor, OnButtonHome); + SAFE_CALL(OH_GamePad_ButtonMenu_RegisterButtonInputMonitor, OnButtonMenu); + SAFE_CALL(OH_GamePad_Dpad_UpButton_RegisterButtonInputMonitor, OnDpadUp); + SAFE_CALL(OH_GamePad_Dpad_DownButton_RegisterButtonInputMonitor, OnDpadDown); + SAFE_CALL(OH_GamePad_Dpad_LeftButton_RegisterButtonInputMonitor, OnDpadLeft); + SAFE_CALL(OH_GamePad_Dpad_RightButton_RegisterButtonInputMonitor, OnDpadRight); // 轴监听 - OH_GamePad_LeftThumbstick_RegisterAxisInputMonitor(OnLeftThumbstickAxis); - OH_GamePad_RightThumbstick_RegisterAxisInputMonitor(OnRightThumbstickAxis); - OH_GamePad_Dpad_RegisterAxisInputMonitor(OnDpadAxis); - OH_GamePad_LeftTrigger_RegisterAxisInputMonitor(OnLeftTriggerAxis); - OH_GamePad_RightTrigger_RegisterAxisInputMonitor(OnRightTriggerAxis); + SAFE_CALL(OH_GamePad_LeftThumbstick_RegisterAxisInputMonitor, OnLeftThumbstickAxis); + SAFE_CALL(OH_GamePad_RightThumbstick_RegisterAxisInputMonitor, OnRightThumbstickAxis); + SAFE_CALL(OH_GamePad_Dpad_RegisterAxisInputMonitor, OnDpadAxis); + SAFE_CALL(OH_GamePad_LeftTrigger_RegisterAxisInputMonitor, OnLeftTriggerAxis); + SAFE_CALL(OH_GamePad_RightTrigger_RegisterAxisInputMonitor, OnRightTriggerAxis); } /** @@ -809,31 +833,34 @@ static void RegisterAllInputMonitors() { */ static void UnregisterAllInputMonitors() { // 按键监听 - OH_GamePad_ButtonA_UnregisterButtonInputMonitor(); - OH_GamePad_ButtonB_UnregisterButtonInputMonitor(); - OH_GamePad_ButtonX_UnregisterButtonInputMonitor(); - OH_GamePad_ButtonY_UnregisterButtonInputMonitor(); - OH_GamePad_ButtonC_UnregisterButtonInputMonitor(); - OH_GamePad_LeftShoulder_UnregisterButtonInputMonitor(); - OH_GamePad_RightShoulder_UnregisterButtonInputMonitor(); - OH_GamePad_LeftTrigger_UnregisterButtonInputMonitor(); - OH_GamePad_RightTrigger_UnregisterButtonInputMonitor(); - OH_GamePad_LeftThumbstick_UnregisterButtonInputMonitor(); - OH_GamePad_RightThumbstick_UnregisterButtonInputMonitor(); - OH_GamePad_ButtonHome_UnregisterButtonInputMonitor(); - OH_GamePad_ButtonMenu_UnregisterButtonInputMonitor(); - OH_GamePad_Dpad_UpButton_UnregisterButtonInputMonitor(); - OH_GamePad_Dpad_DownButton_UnregisterButtonInputMonitor(); - OH_GamePad_Dpad_LeftButton_UnregisterButtonInputMonitor(); - OH_GamePad_Dpad_RightButton_UnregisterButtonInputMonitor(); + SAFE_CALL0(OH_GamePad_ButtonA_UnregisterButtonInputMonitor); + SAFE_CALL0(OH_GamePad_ButtonB_UnregisterButtonInputMonitor); + SAFE_CALL0(OH_GamePad_ButtonX_UnregisterButtonInputMonitor); + SAFE_CALL0(OH_GamePad_ButtonY_UnregisterButtonInputMonitor); + SAFE_CALL0(OH_GamePad_ButtonC_UnregisterButtonInputMonitor); + SAFE_CALL0(OH_GamePad_LeftShoulder_UnregisterButtonInputMonitor); + SAFE_CALL0(OH_GamePad_RightShoulder_UnregisterButtonInputMonitor); + SAFE_CALL0(OH_GamePad_LeftTrigger_UnregisterButtonInputMonitor); + SAFE_CALL0(OH_GamePad_RightTrigger_UnregisterButtonInputMonitor); + SAFE_CALL0(OH_GamePad_LeftThumbstick_UnregisterButtonInputMonitor); + SAFE_CALL0(OH_GamePad_RightThumbstick_UnregisterButtonInputMonitor); + SAFE_CALL0(OH_GamePad_ButtonHome_UnregisterButtonInputMonitor); + SAFE_CALL0(OH_GamePad_ButtonMenu_UnregisterButtonInputMonitor); + SAFE_CALL0(OH_GamePad_Dpad_UpButton_UnregisterButtonInputMonitor); + SAFE_CALL0(OH_GamePad_Dpad_DownButton_UnregisterButtonInputMonitor); + SAFE_CALL0(OH_GamePad_Dpad_LeftButton_UnregisterButtonInputMonitor); + SAFE_CALL0(OH_GamePad_Dpad_RightButton_UnregisterButtonInputMonitor); // 轴监听 - OH_GamePad_LeftThumbstick_UnregisterAxisInputMonitor(); - OH_GamePad_RightThumbstick_UnregisterAxisInputMonitor(); - OH_GamePad_Dpad_UnregisterAxisInputMonitor(); - OH_GamePad_LeftTrigger_UnregisterAxisInputMonitor(); - OH_GamePad_RightTrigger_UnregisterAxisInputMonitor(); + SAFE_CALL0(OH_GamePad_LeftThumbstick_UnregisterAxisInputMonitor); + SAFE_CALL0(OH_GamePad_RightThumbstick_UnregisterAxisInputMonitor); + SAFE_CALL0(OH_GamePad_Dpad_UnregisterAxisInputMonitor); + SAFE_CALL0(OH_GamePad_LeftTrigger_UnregisterAxisInputMonitor); + SAFE_CALL0(OH_GamePad_RightTrigger_UnregisterAxisInputMonitor); } + +#undef SAFE_CALL +#undef SAFE_CALL0 #endif // GAME_CONTROLLER_KIT_AVAILABLE // ==================== 公共 API ==================== From 2762b2fcc26a5d11e46ac859a1f085439e05380b Mon Sep 17 00:00:00 2001 From: qiin2333 <414382190@qq.com> Date: Thu, 2 Apr 2026 17:06:28 +0800 Subject: [PATCH 8/9] =?UTF-8?q?fix(ci):=20=E7=A7=BB=E9=99=A4=20workflow=20?= =?UTF-8?q?=E6=9C=AB=E5=B0=BE=E5=AD=A4=E7=AB=8B=20YAML=20=E6=98=A0?= =?UTF-8?q?=E5=B0=84=E9=94=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9afc1ce..21b6606 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -334,6 +334,3 @@ jobs: retention-days: 7 # Release 由本地手动构建签名后上传 - fail_on_unmatched_files: false - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 62e576e58e885599dde199b388836c8af3a41a50 Mon Sep 17 00:00:00 2001 From: qiin2333 <414382190@qq.com> Date: Thu, 2 Apr 2026 17:07:26 +0800 Subject: [PATCH 9/9] =?UTF-8?q?fix:=20GameController=5FStopMonitor=20?= =?UTF-8?q?=E7=AB=9E=E6=80=81=E4=BF=AE=E5=A4=8D=20-=20=E5=88=86=E7=A6=BB?= =?UTF-8?q?=E5=8A=A0=E9=94=81/=E6=97=A0=E9=94=81=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - NapiStopMonitor 路径现在正确持有 g_mutex - Uninit 内部调用无锁版 GameController_StopMonitorUnlocked 避免死锁 --- .../src/main/cpp/game_controller_native.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/nativelib/src/main/cpp/game_controller_native.cpp b/nativelib/src/main/cpp/game_controller_native.cpp index 93e844c..9637ae3 100644 --- a/nativelib/src/main/cpp/game_controller_native.cpp +++ b/nativelib/src/main/cpp/game_controller_native.cpp @@ -904,13 +904,16 @@ int GameController_Init(void) { #endif } +// Forward declaration - defined after GameController_StopMonitor +static void GameController_StopMonitorUnlocked(); + void GameController_Uninit(void) { std::lock_guard lock(g_mutex); if (!g_initialized) return; if (g_gcLibAvailable) { - GameController_StopMonitor(); + GameController_StopMonitorUnlocked(); } g_deviceStates.clear(); @@ -1034,10 +1037,9 @@ int GameController_StartMonitor(void) { #endif } -void GameController_StopMonitor(void) { +// 无锁版本,供已持有 g_mutex 的调用者使用(如 Uninit) +static void GameController_StopMonitorUnlocked() { #if GAME_CONTROLLER_KIT_AVAILABLE - // 注意: 此函数可能在 Uninit 的锁内被调用,不要再加锁 - if (!g_monitoring) return; if (!g_gcLibAvailable) { @@ -1059,6 +1061,13 @@ void GameController_StopMonitor(void) { #endif } +void GameController_StopMonitor(void) { +#if GAME_CONTROLLER_KIT_AVAILABLE + std::lock_guard lock(g_mutex); + GameController_StopMonitorUnlocked(); +#endif +} + /** * 暂停输入监听(仅按键+轴),保留设备上下线监听 * 用于无手柄场景减少系统轮询干扰,同时保持设备热插拔检测