Skip to content

Commit 2e36c5e

Browse files
committed
feat(window-recognition): 完整信号注入(进程/几何/前台窗口,native缺失降级)
1 parent 3017f52 commit 2e36c5e

1 file changed

Lines changed: 59 additions & 5 deletions

File tree

‎client/electron/screenCaptureHandlers.ts‎

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,17 @@
88
* @ai-context: 帧数据经 sender 窗口 webContents.send 推回(非广播),
99
* 窗口销毁时静默丢帧。
1010
*/
11-
import { BrowserWindow, desktopCapturer } from 'electron';
11+
import { BrowserWindow, desktopCapturer, screen } from 'electron';
1212
import { ScreenCapture } from './screenCapture.js';
1313
import type { ScreenCaptureOptions, ScreenshotFrameData } from './screenCapture.js';
1414
import { safeHandle, getMainWindowId } from './ipcUtils.js';
1515
import { logger } from './logger.js';
1616
import { scoreAndFilterWindows } from './windowScorer.js';
1717
import type { WindowSignalInput } from './windowRules.js';
1818
import { getCaptureRateScale, onPerformanceModeChange } from './performanceMode.js';
19+
import { loadProcessAudioNative } from './audio/processAudioNative.js';
20+
import { parseHwndFromSourceId, buildNativeIndex, resolveGeometrySignals } from './windowSignals.js';
21+
import type { NativeWindowSignal } from './windowSignals.js';
1922

2023
// ================================================================
2124
// 模块级状态
@@ -67,12 +70,63 @@ function applyRateScale(options: ScreenCaptureOptions): ScreenCaptureOptions {
6770

6871
/**
6972
* 构建 source id → 评分信号的映射。
70-
* @ai-context: 阶段一为空映射(纯标题评分);阶段二接入 native 窗口枚举后填充
71-
* 进程名/几何/前台信号。信号源缺失时返回空 Map,评分自动降级。
73+
* @ai-context: native 缺失时返回空 Map,评分自动降级为纯标题(与旧版行为一致)。
74+
* 前台窗口判定:native.getForegroundHwnd() 命中当前 source 的 HWND。
7275
*/
7376
function buildSignalMap(sources: Electron.DesktopCapturerSource[]): Map<string, WindowSignalInput> {
74-
void sources;
75-
return new Map();
77+
const signals = new Map<string, WindowSignalInput>();
78+
const native = loadProcessAudioNative();
79+
if (!native) return signals;
80+
81+
let nativeWindows: NativeWindowSignal[];
82+
try {
83+
nativeWindows = native.listAudioWindows().map((w) => ({
84+
hwnd: String(w.hwnd),
85+
processName: w.processName,
86+
width: w.width,
87+
height: w.height,
88+
alwaysOnTop: w.alwaysOnTop,
89+
}));
90+
} catch (err) {
91+
logger.warn(`[IPC] native listAudioWindows 失败,降级纯标题评分: ${err instanceof Error ? err.message : String(err)}`);
92+
return signals;
93+
}
94+
95+
const index = buildNativeIndex(nativeWindows);
96+
97+
// 显示器总面积(像素²;无显示器时置 0,面积占比信号自动跳过)
98+
let displayArea = 0;
99+
try {
100+
const bounds = screen.getPrimaryDisplay().bounds;
101+
displayArea = bounds.width * bounds.height;
102+
} catch {
103+
displayArea = 0;
104+
}
105+
106+
// 前台窗口 hwnd(失败时为空串 → 无窗口命中前台)
107+
let foregroundHwnd = '';
108+
try {
109+
foregroundHwnd = native.getForegroundHwnd();
110+
} catch {
111+
foregroundHwnd = '';
112+
}
113+
114+
for (const src of sources) {
115+
const hwnd = parseHwndFromSourceId(src.id);
116+
if (!hwnd) continue;
117+
const nativeWin = index.get(hwnd);
118+
if (!nativeWin) continue;
119+
const geo = resolveGeometrySignals(nativeWin, displayArea);
120+
signals.set(src.id, {
121+
title: src.name,
122+
processName: nativeWin.processName,
123+
aspectRatio: geo.aspectRatio,
124+
areaRatio: geo.areaRatio,
125+
alwaysOnTop: geo.alwaysOnTop,
126+
isForeground: hwnd === foregroundHwnd,
127+
});
128+
}
129+
return signals;
76130
}
77131

78132
/** 销毁旧实例并按其参数/推送目标重建采集(start 与性能模式重启共用) */

0 commit comments

Comments
 (0)