diff --git a/entry/src/main/ets/pages/StreamPage.ets b/entry/src/main/ets/pages/StreamPage.ets index 81ce925..67e7a07 100644 --- a/entry/src/main/ets/pages/StreamPage.ets +++ b/entry/src/main/ets/pages/StreamPage.ets @@ -227,6 +227,9 @@ struct StreamPage { private enableBackgroundTask: boolean = true; // 后台保活开关 private endStreamReportEnabled: boolean = false; // 结束串流显示数据/战报开关 private streamStartedAtMs: number = 0; // 本次串流真正连上后的时间戳 + private pendingStreamShutdown: Promise | null = null; + private pendingStreamShutdownLabel: string = ''; + private isClosingStreamReport: boolean = false; // ── Network Boost 场景监听(弱信号/拥塞 提示) ── private netSceneListener: SystemNetSceneListener | null = null; @@ -763,9 +766,11 @@ struct StreamPage { this.resumeMouseInterceptor(); }, onDisconnect: () => { + this.gameMenuDialogController?.close(); this.stopStreaming(); }, onQuitGame: () => { + this.gameMenuDialogController?.close(); this.quitGameAndReturn(); }, onTouchModeChanged: (mode: string) => { @@ -1376,10 +1381,9 @@ struct StreamPage { } /** - * 串流结束后仍停留在页面展示战报,因此需要先主动释放输入/后台资源并恢复窗口。 + * 立即停止当前页面上的交互输入,确保战报能先接管界面。 */ - private async prepareStreamEndUi(): Promise { - await this.lifecycleManager.stopBackgroundTask(); + private prepareStreamEndUiImmediate(): void { this.cancelUnhandledUsbDetection(); this.inputHandler.resetState(); this.inputHandler.stopMouseInterceptor(); @@ -1387,51 +1391,141 @@ struct StreamPage { try { pointer.setPointerVisibleSync(true); } catch (_) {} + } + + /** + * 无战报模式下,仍沿用原来的前台清理流程:停止后台保活并恢复窗口。 + */ + private async prepareStreamEndUi(): Promise { + this.prepareStreamEndUiImmediate(); + await this.lifecycleManager.stopBackgroundTask(); await this.windowManager?.restoreWindow(); } + /** + * 战报展示期间,真正的停流/退游戏在后台继续执行;关闭战报前再等待其收尾。 + */ + private runStreamShutdownInBackground(label: string, task: () => Promise): void { + if (this.pendingStreamShutdown) { + console.info(`[StreamPage] ${label}流程已在进行中,忽略重复请求`); + return; + } + + this.pendingStreamShutdownLabel = label; + this.pendingStreamShutdown = (async (): Promise => { + try { + await task(); + } catch (err) { + console.error(`[StreamPage] ${label}后台收尾失败:`, err); + } finally { + this.pendingStreamShutdown = null; + this.pendingStreamShutdownLabel = ''; + } + })(); + } + + private runDetachedAsyncTask(label: string, task: () => Promise): void { + task().catch((err: Error) => { + console.error(`[StreamPage] ${label}失败:`, err); + }); + } + + private async waitForPendingStreamShutdown(): Promise { + const pending = this.pendingStreamShutdown; + if (!pending) { + return; + } + + ToastQueue.show({ + message: this.pendingStreamShutdownLabel + ? `${this.pendingStreamShutdownLabel},请稍候…` + : '正在结束串流…', + duration: 1200 + }); + await pending; + } + + private async sendLockScreenShortcutBeforeDisconnect(): Promise { + if (!this.viewModel.streamConfig?.lockScreenAfterDisconnect) { + return; + } + + this.viewModel.getSession()?.sendKeys([0x5B, 0x4C]); // VK_LWIN + VK_L + await new Promise((resolve) => setTimeout(resolve, 500)); + } + private async closeStreamReportAndReturn(): Promise { - this.showStreamReport = false; - await this.exitStreamAndReturn(); + if (this.isClosingStreamReport) { + return; + } + + this.isClosingStreamReport = true; + try { + await this.waitForPendingStreamShutdown(); + this.showStreamReport = false; + await this.exitStreamAndReturn(); + } catch (err) { + this.isClosingStreamReport = false; + console.error('[StreamPage] 关闭串流战报失败:', err); + } } async stopStreaming(): Promise { + if (this.pendingStreamShutdown) { + console.info('[StreamPage] stopStreaming 已在执行,忽略重复调用'); + return; + } + if (!this.endStreamReportEnabled) { await this.prepareStreamEndUi(); await this.exitStreamAndReturn(); - this.viewModel.stopStreaming(); + this.runDetachedAsyncTask('后台停止串流', (): Promise => this.viewModel.stopStreaming()); return; } const report = this.buildCurrentStreamReport(); - await this.prepareStreamEndUi(); - await this.viewModel.stopStreaming(); + this.prepareStreamEndUiImmediate(); this.streamReportData = report; this.showStreamReport = true; + this.runStreamShutdownInBackground('正在断开串流', async (): Promise => { + await Promise.all([ + this.lifecycleManager.stopBackgroundTask(), + this.viewModel.stopStreaming() + ]); + }); } /** * 退出游戏并返回(请求服务器退出当前游戏,显示串流战报后再返回列表) */ async quitGameAndReturn(): Promise { - const report = this.endStreamReportEnabled ? this.buildCurrentStreamReport() : null; - await this.prepareStreamEndUi(); - - // 退出前发送 Win+L 锁定主机屏幕,延迟确保命令送达 - if (this.viewModel.streamConfig?.lockScreenAfterDisconnect) { - this.viewModel.getSession()?.sendKeys([0x5B, 0x4C]); // VK_LWIN + VK_L - await new Promise((resolve) => setTimeout(resolve, 500)); + if (this.pendingStreamShutdown) { + console.info('[StreamPage] quitGameAndReturn 已在执行,忽略重复调用'); + return; } + const report = this.endStreamReportEnabled ? this.buildCurrentStreamReport() : null; + this.prepareStreamEndUiImmediate(); + if (!this.endStreamReportEnabled || !report) { + await this.lifecycleManager.stopBackgroundTask(); + await this.sendLockScreenShortcutBeforeDisconnect(); await this.exitStreamAndReturn(); - this.viewModel.quitGame().then(() => {}).catch((_err: Error) => {}); + this.runDetachedAsyncTask('后台退出游戏', (): Promise => this.viewModel.quitGame()); return; } - await this.viewModel.quitGame(); this.streamReportData = report; this.showStreamReport = true; + this.runStreamShutdownInBackground('正在退出游戏', async (): Promise => { + const stopBackgroundTaskPromise = this.lifecycleManager.stopBackgroundTask(); + await this.sendLockScreenShortcutBeforeDisconnect(); + + await Promise.all([ + stopBackgroundTaskPromise, + this.viewModel.quitGame() + ]); + }); } /**