From 1d4b3b4ed097a06dea9218f7a42aa35812093f45 Mon Sep 17 00:00:00 2001 From: Kyle Date: Fri, 10 Apr 2026 19:26:47 -0700 Subject: [PATCH] refactor: simplify Windows tracker helper API --- scripts/get-mpv-window-windows.ps1 | 401 --------------- scripts/prepare-build-assets.mjs | 8 - src/window-trackers/windows-helper.test.ts | 347 ++----------- src/window-trackers/windows-helper.ts | 564 +-------------------- src/window-trackers/windows-tracker.ts | 16 +- 5 files changed, 62 insertions(+), 1274 deletions(-) delete mode 100644 scripts/get-mpv-window-windows.ps1 diff --git a/scripts/get-mpv-window-windows.ps1 b/scripts/get-mpv-window-windows.ps1 deleted file mode 100644 index e0a041a5..00000000 --- a/scripts/get-mpv-window-windows.ps1 +++ /dev/null @@ -1,401 +0,0 @@ -param( - [ValidateSet('geometry', 'foreground-process', 'bind-overlay', 'lower-overlay', 'set-owner', 'clear-owner', 'target-hwnd')] - [string]$Mode = 'geometry', - [string]$SocketPath, - [string]$OverlayWindowHandle -) - -$ErrorActionPreference = 'Stop' - -try { - Add-Type -TypeDefinition @" -using System; -using System.Runtime.InteropServices; - -public static class SubMinerWindowsHelper { - public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); - - [StructLayout(LayoutKind.Sequential)] - public struct RECT { - public int Left; - public int Top; - public int Right; - public int Bottom; - } - - [DllImport("user32.dll")] - public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); - - [DllImport("user32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool IsWindowVisible(IntPtr hWnd); - - [DllImport("user32.dll")] - public static extern bool IsIconic(IntPtr hWnd); - - [DllImport("user32.dll")] - public static extern IntPtr GetForegroundWindow(); - - [DllImport("user32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool SetWindowPos( - IntPtr hWnd, - IntPtr hWndInsertAfter, - int X, - int Y, - int cx, - int cy, - uint uFlags - ); - - [DllImport("user32.dll", SetLastError = true)] - public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId); - - [DllImport("user32.dll", SetLastError = true)] - public static extern int GetWindowLong(IntPtr hWnd, int nIndex); - - [DllImport("user32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect); - - [DllImport("user32.dll", SetLastError = true)] - public static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd); - - [DllImport("user32.dll", SetLastError = true)] - public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong); - - [DllImport("kernel32.dll")] - public static extern void SetLastError(uint dwErrCode); - - [DllImport("dwmapi.dll")] - public static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out RECT pvAttribute, int cbAttribute); -} -"@ - - $DWMWA_EXTENDED_FRAME_BOUNDS = 9 - $SWP_NOSIZE = 0x0001 - $SWP_NOMOVE = 0x0002 - $SWP_NOACTIVATE = 0x0010 - $SWP_NOOWNERZORDER = 0x0200 - $SWP_FLAGS = $SWP_NOSIZE -bor $SWP_NOMOVE -bor $SWP_NOACTIVATE -bor $SWP_NOOWNERZORDER - $GWL_EXSTYLE = -20 - $WS_EX_TOPMOST = 0x00000008 - $GWLP_HWNDPARENT = -8 - $HWND_TOP = [IntPtr]::Zero - $HWND_BOTTOM = [IntPtr]::One - $HWND_TOPMOST = [IntPtr](-1) - $HWND_NOTOPMOST = [IntPtr](-2) - - function Assert-SetWindowLongPtrSucceeded { - param( - [IntPtr]$Result, - [string]$Operation - ) - - if ($Result -ne [IntPtr]::Zero) { - return - } - - if ([Runtime.InteropServices.Marshal]::GetLastWin32Error() -eq 0) { - return - } - - $lastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error() - throw "$Operation failed ($lastError)" - } - - function Assert-SetWindowPosSucceeded { - param( - [bool]$Result, - [string]$Operation - ) - - if ($Result) { - return - } - - $lastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error() - throw "$Operation failed ($lastError)" - } - - if ($Mode -eq 'foreground-process') { - $foregroundWindow = [SubMinerWindowsHelper]::GetForegroundWindow() - if ($foregroundWindow -eq [IntPtr]::Zero) { - Write-Output 'not-found' - exit 0 - } - - [uint32]$foregroundProcessId = 0 - [void][SubMinerWindowsHelper]::GetWindowThreadProcessId($foregroundWindow, [ref]$foregroundProcessId) - if ($foregroundProcessId -eq 0) { - Write-Output 'not-found' - exit 0 - } - - try { - $foregroundProcess = Get-Process -Id $foregroundProcessId -ErrorAction Stop - } catch { - Write-Output 'not-found' - exit 0 - } - - Write-Output "process=$($foregroundProcess.ProcessName)" - exit 0 - } - - if ($Mode -eq 'clear-owner') { - if ([string]::IsNullOrWhiteSpace($OverlayWindowHandle)) { - [Console]::Error.WriteLine('overlay-window-handle-required') - exit 1 - } - - [IntPtr]$overlayWindow = [IntPtr]([int64]$OverlayWindowHandle) - [SubMinerWindowsHelper]::SetLastError(0) - $result = [SubMinerWindowsHelper]::SetWindowLongPtr($overlayWindow, $GWLP_HWNDPARENT, [IntPtr]::Zero) - Assert-SetWindowLongPtrSucceeded -Result $result -Operation 'clear-owner' - Write-Output 'ok' - exit 0 - } - - function Get-WindowBounds { - param([IntPtr]$hWnd) - - $rect = New-Object SubMinerWindowsHelper+RECT - $size = [System.Runtime.InteropServices.Marshal]::SizeOf($rect) - $dwmResult = [SubMinerWindowsHelper]::DwmGetWindowAttribute( - $hWnd, - $DWMWA_EXTENDED_FRAME_BOUNDS, - [ref]$rect, - $size - ) - - if ($dwmResult -ne 0) { - if (-not [SubMinerWindowsHelper]::GetWindowRect($hWnd, [ref]$rect)) { - return $null - } - } - - $width = $rect.Right - $rect.Left - $height = $rect.Bottom - $rect.Top - if ($width -le 0 -or $height -le 0) { - return $null - } - - return [PSCustomObject]@{ - X = $rect.Left - Y = $rect.Top - Width = $width - Height = $height - Area = $width * $height - } - } - - $commandLineByPid = @{} - if (-not [string]::IsNullOrWhiteSpace($SocketPath)) { - foreach ($process in Get-CimInstance Win32_Process) { - $commandLineByPid[[uint32]$process.ProcessId] = $process.CommandLine - } - } - - $mpvMatches = New-Object System.Collections.Generic.List[object] - $targetWindowState = 'not-found' - $foregroundWindow = [SubMinerWindowsHelper]::GetForegroundWindow() - $callback = [SubMinerWindowsHelper+EnumWindowsProc]{ - param([IntPtr]$hWnd, [IntPtr]$lParam) - - if (-not [SubMinerWindowsHelper]::IsWindowVisible($hWnd)) { - return $true - } - - [uint32]$windowProcessId = 0 - [void][SubMinerWindowsHelper]::GetWindowThreadProcessId($hWnd, [ref]$windowProcessId) - if ($windowProcessId -eq 0) { - return $true - } - - try { - $process = Get-Process -Id $windowProcessId -ErrorAction Stop - } catch { - return $true - } - - if ($process.ProcessName -ine 'mpv') { - return $true - } - - if (-not [string]::IsNullOrWhiteSpace($SocketPath)) { - $commandLine = $commandLineByPid[[uint32]$windowProcessId] - if ([string]::IsNullOrWhiteSpace($commandLine)) { - return $true - } - if ( - ($commandLine -notlike "*--input-ipc-server=$SocketPath*") -and - ($commandLine -notlike "*--input-ipc-server $SocketPath*") - ) { - return $true - } - } - - if ([SubMinerWindowsHelper]::IsIconic($hWnd)) { - if (-not [string]::IsNullOrWhiteSpace($SocketPath) -and $targetWindowState -ne 'visible') { - $targetWindowState = 'minimized' - } - return $true - } - - $bounds = Get-WindowBounds -hWnd $hWnd - if ($null -eq $bounds) { - return $true - } - - if (-not [string]::IsNullOrWhiteSpace($SocketPath)) { - $targetWindowState = 'visible' - } - - $mpvMatches.Add([PSCustomObject]@{ - HWnd = $hWnd - X = $bounds.X - Y = $bounds.Y - Width = $bounds.Width - Height = $bounds.Height - Area = $bounds.Area - IsForeground = ($foregroundWindow -ne [IntPtr]::Zero -and $hWnd -eq $foregroundWindow) - }) - - return $true - } - - [void][SubMinerWindowsHelper]::EnumWindows($callback, [IntPtr]::Zero) - - if ($Mode -eq 'lower-overlay') { - if ([string]::IsNullOrWhiteSpace($OverlayWindowHandle)) { - [Console]::Error.WriteLine('overlay-window-handle-required') - exit 1 - } - - [IntPtr]$overlayWindow = [IntPtr]([int64]$OverlayWindowHandle) - - [void][SubMinerWindowsHelper]::SetWindowPos( - $overlayWindow, - $HWND_NOTOPMOST, - 0, - 0, - 0, - 0, - $SWP_FLAGS - ) - [void][SubMinerWindowsHelper]::SetWindowPos( - $overlayWindow, - $HWND_BOTTOM, - 0, - 0, - 0, - 0, - $SWP_FLAGS - ) - Write-Output 'ok' - exit 0 - } - - $focusedMatch = $mpvMatches | Where-Object { $_.IsForeground } | Select-Object -First 1 - if ($null -ne $focusedMatch) { - [Console]::Error.WriteLine('focus=focused') - } else { - [Console]::Error.WriteLine('focus=not-focused') - } - if (-not [string]::IsNullOrWhiteSpace($SocketPath)) { - [Console]::Error.WriteLine("state=$targetWindowState") - } - - if ($mpvMatches.Count -eq 0) { - Write-Output 'not-found' - exit 0 - } - - $bestMatch = if ($null -ne $focusedMatch) { - $focusedMatch - } else { - $mpvMatches | Sort-Object -Property Area, Width, Height -Descending | Select-Object -First 1 - } - - if ($Mode -eq 'target-hwnd') { - Write-Output "$($bestMatch.HWnd)" - exit 0 - } - - if ($Mode -eq 'set-owner') { - if ([string]::IsNullOrWhiteSpace($OverlayWindowHandle)) { - [Console]::Error.WriteLine('overlay-window-handle-required') - exit 1 - } - - [IntPtr]$overlayWindow = [IntPtr]([int64]$OverlayWindowHandle) - $targetWindow = [IntPtr]$bestMatch.HWnd - [SubMinerWindowsHelper]::SetLastError(0) - $result = [SubMinerWindowsHelper]::SetWindowLongPtr($overlayWindow, $GWLP_HWNDPARENT, $targetWindow) - Assert-SetWindowLongPtrSucceeded -Result $result -Operation 'set-owner' - Write-Output 'ok' - exit 0 - } - - if ($Mode -eq 'bind-overlay') { - if ([string]::IsNullOrWhiteSpace($OverlayWindowHandle)) { - [Console]::Error.WriteLine('overlay-window-handle-required') - exit 1 - } - - [IntPtr]$overlayWindow = [IntPtr]([int64]$OverlayWindowHandle) - $targetWindow = [IntPtr]$bestMatch.HWnd - [SubMinerWindowsHelper]::SetLastError(0) - $result = [SubMinerWindowsHelper]::SetWindowLongPtr($overlayWindow, $GWLP_HWNDPARENT, $targetWindow) - Assert-SetWindowLongPtrSucceeded -Result $result -Operation 'bind-overlay owner assignment' - $targetWindowExStyle = [SubMinerWindowsHelper]::GetWindowLong($targetWindow, $GWL_EXSTYLE) - $targetWindowIsTopmost = ($targetWindowExStyle -band $WS_EX_TOPMOST) -ne 0 - - $overlayExStyle = [SubMinerWindowsHelper]::GetWindowLong($overlayWindow, $GWL_EXSTYLE) - $overlayIsTopmost = ($overlayExStyle -band $WS_EX_TOPMOST) -ne 0 - if ($targetWindowIsTopmost -and -not $overlayIsTopmost) { - [SubMinerWindowsHelper]::SetLastError(0) - $result = [SubMinerWindowsHelper]::SetWindowPos( - $overlayWindow, $HWND_TOPMOST, 0, 0, 0, 0, $SWP_FLAGS - ) - Assert-SetWindowPosSucceeded -Result $result -Operation 'bind-overlay topmost adjustment' - } elseif (-not $targetWindowIsTopmost -and $overlayIsTopmost) { - [SubMinerWindowsHelper]::SetLastError(0) - $result = [SubMinerWindowsHelper]::SetWindowPos( - $overlayWindow, $HWND_NOTOPMOST, 0, 0, 0, 0, $SWP_FLAGS - ) - Assert-SetWindowPosSucceeded -Result $result -Operation 'bind-overlay notopmost adjustment' - } - - $GW_HWNDPREV = 3 - $windowAboveMpv = [SubMinerWindowsHelper]::GetWindow($targetWindow, $GW_HWNDPREV) - - if ($windowAboveMpv -ne [IntPtr]::Zero -and $windowAboveMpv -eq $overlayWindow) { - Write-Output 'ok' - exit 0 - } - - $insertAfter = $HWND_TOP - if ($windowAboveMpv -ne [IntPtr]::Zero) { - $aboveExStyle = [SubMinerWindowsHelper]::GetWindowLong($windowAboveMpv, $GWL_EXSTYLE) - $aboveIsTopmost = ($aboveExStyle -band $WS_EX_TOPMOST) -ne 0 - if ($aboveIsTopmost -eq $targetWindowIsTopmost) { - $insertAfter = $windowAboveMpv - } - } - - [SubMinerWindowsHelper]::SetLastError(0) - $result = [SubMinerWindowsHelper]::SetWindowPos( - $overlayWindow, $insertAfter, 0, 0, 0, 0, $SWP_FLAGS - ) - Assert-SetWindowPosSucceeded -Result $result -Operation 'bind-overlay z-order adjustment' - Write-Output 'ok' - exit 0 - } - - Write-Output "$($bestMatch.X),$($bestMatch.Y),$($bestMatch.Width),$($bestMatch.Height)" -} catch { - [Console]::Error.WriteLine($_.Exception.Message) - exit 1 -} diff --git a/scripts/prepare-build-assets.mjs b/scripts/prepare-build-assets.mjs index 38cad99f..575a2869 100644 --- a/scripts/prepare-build-assets.mjs +++ b/scripts/prepare-build-assets.mjs @@ -8,8 +8,6 @@ const repoRoot = path.resolve(scriptDir, '..'); const rendererSourceDir = path.join(repoRoot, 'src', 'renderer'); const rendererOutputDir = path.join(repoRoot, 'dist', 'renderer'); const scriptsOutputDir = path.join(repoRoot, 'dist', 'scripts'); -const windowsHelperSourcePath = path.join(scriptDir, 'get-mpv-window-windows.ps1'); -const windowsHelperOutputPath = path.join(scriptsOutputDir, 'get-mpv-window-windows.ps1'); const macosHelperSourcePath = path.join(scriptDir, 'get-mpv-window-macos.swift'); const macosHelperBinaryPath = path.join(scriptsOutputDir, 'get-mpv-window-macos'); const macosHelperSourceCopyPath = path.join(scriptsOutputDir, 'get-mpv-window-macos.swift'); @@ -33,11 +31,6 @@ function copyRendererAssets() { process.stdout.write(`Staged renderer assets in ${rendererOutputDir}\n`); } -function stageWindowsHelper() { - copyFile(windowsHelperSourcePath, windowsHelperOutputPath); - process.stdout.write(`Staged Windows helper: ${windowsHelperOutputPath}\n`); -} - function fallbackToMacosSource() { copyFile(macosHelperSourcePath, macosHelperSourceCopyPath); process.stdout.write(`Staged macOS helper source fallback: ${macosHelperSourceCopyPath}\n`); @@ -77,7 +70,6 @@ function buildMacosHelper() { function main() { copyRendererAssets(); - stageWindowsHelper(); buildMacosHelper(); } diff --git a/src/window-trackers/windows-helper.test.ts b/src/window-trackers/windows-helper.test.ts index ea08532d..88a0c4ab 100644 --- a/src/window-trackers/windows-helper.test.ts +++ b/src/window-trackers/windows-helper.test.ts @@ -1,323 +1,60 @@ import test from 'node:test'; import assert from 'node:assert/strict'; -import * as windowsHelper from './windows-helper'; -import { - lowerWindowsOverlayInZOrder, - parseWindowTrackerHelperForegroundProcess, - parseWindowTrackerHelperFocusState, - parseWindowTrackerHelperOutput, - parseWindowTrackerHelperState, - queryWindowsForegroundProcessName, - queryWindowsTargetWindowHandle, - queryWindowsTrackerMpvWindows, - resolveWindowsTrackerHelper, - syncWindowsOverlayToMpvZOrder, -} from './windows-helper'; +import { findWindowsMpvTargetWindowHandle } from './windows-helper'; +import type { MpvPollResult } from './win32'; -test('parseWindowTrackerHelperOutput parses helper geometry output', () => { - assert.deepEqual(parseWindowTrackerHelperOutput('120,240,1280,720'), { - x: 120, - y: 240, - width: 1280, - height: 720, - }); -}); - -test('parseWindowTrackerHelperOutput returns null for misses and invalid payloads', () => { - assert.equal(parseWindowTrackerHelperOutput('not-found'), null); - assert.equal(parseWindowTrackerHelperOutput('1,2,3'), null); - assert.equal(parseWindowTrackerHelperOutput('1,2,0,4'), null); -}); - -test('parseWindowTrackerHelperFocusState parses helper stderr metadata', () => { - assert.equal(parseWindowTrackerHelperFocusState('focus=focused'), true); - assert.equal(parseWindowTrackerHelperFocusState('focus=not-focused'), false); - assert.equal(parseWindowTrackerHelperFocusState('warning\nfocus=focused\nnote'), true); - assert.equal(parseWindowTrackerHelperFocusState(''), null); -}); - -test('parseWindowTrackerHelperState parses helper stderr metadata', () => { - assert.equal(parseWindowTrackerHelperState('state=visible'), 'visible'); - assert.equal(parseWindowTrackerHelperState('focus=not-focused\nstate=minimized'), 'minimized'); - assert.equal(parseWindowTrackerHelperState('state=unknown'), null); - assert.equal(parseWindowTrackerHelperState(''), null); -}); - -test('parseWindowTrackerHelperForegroundProcess parses helper stdout metadata', () => { - assert.equal(parseWindowTrackerHelperForegroundProcess('process=mpv'), 'mpv'); - assert.equal(parseWindowTrackerHelperForegroundProcess('process=chrome'), 'chrome'); - assert.equal(parseWindowTrackerHelperForegroundProcess('not-found'), null); - assert.equal(parseWindowTrackerHelperForegroundProcess(''), null); -}); - -test('queryWindowsForegroundProcessName reads foreground process from powershell helper', async () => { - const processName = await queryWindowsForegroundProcessName({ - resolveHelper: () => ({ - kind: 'powershell', - command: 'powershell.exe', - args: ['-File', 'helper.ps1'], - helperPath: 'helper.ps1', - }), - runHelper: async () => ({ - stdout: 'process=mpv', - stderr: '', - }), - }); - - assert.equal(processName, 'mpv'); -}); - -test('queryWindowsForegroundProcessName returns null when no powershell helper is available', async () => { - const processName = await queryWindowsForegroundProcessName({ - resolveHelper: () => ({ - kind: 'native', - command: 'helper.exe', - args: [], - helperPath: 'helper.exe', - }), - }); - - assert.equal(processName, null); -}); - -test('syncWindowsOverlayToMpvZOrder forwards socket path and overlay handle to powershell helper', async () => { - let capturedMode: string | null = null; - let capturedArgs: string[] | null = null; - - const synced = await syncWindowsOverlayToMpvZOrder({ - overlayWindowHandle: '12345', - targetMpvSocketPath: '\\\\.\\pipe\\subminer-socket', - resolveHelper: () => ({ - kind: 'powershell', - command: 'powershell.exe', - args: ['-File', 'helper.ps1'], - helperPath: 'helper.ps1', - }), - runHelper: async (_spec, mode, extraArgs = []) => { - capturedMode = mode; - capturedArgs = extraArgs; - return { - stdout: 'ok', - stderr: '', - }; - }, - }); - - assert.equal(synced, true); - assert.equal(capturedMode, 'bind-overlay'); - assert.deepEqual(capturedArgs, ['\\\\.\\pipe\\subminer-socket', '12345']); -}); - -test('lowerWindowsOverlayInZOrder forwards overlay handle to powershell helper', async () => { - let capturedMode: string | null = null; - let capturedArgs: string[] | null = null; - - const lowered = await lowerWindowsOverlayInZOrder({ - overlayWindowHandle: '67890', - resolveHelper: () => ({ - kind: 'powershell', - command: 'powershell.exe', - args: ['-File', 'helper.ps1'], - helperPath: 'helper.ps1', - }), - runHelper: async (_spec, mode, extraArgs = []) => { - capturedMode = mode; - capturedArgs = extraArgs; - return { - stdout: 'ok', - stderr: '', - }; - }, - }); - - assert.equal(lowered, true); - assert.equal(capturedMode, 'lower-overlay'); - assert.deepEqual(capturedArgs, ['', '67890']); -}); - -test('queryWindowsTrackerMpvWindows resolves geometry from the powershell helper', () => { - let capturedMode: string | null = null; - let capturedArgs: string[] | null = null; - - const result = queryWindowsTrackerMpvWindows({ - targetMpvSocketPath: '\\\\.\\pipe\\subminer-socket', - resolveHelper: () => ({ - kind: 'powershell', - command: 'powershell.exe', - args: ['-File', 'helper.ps1'], - helperPath: 'helper.ps1', - }), - runHelperSync: (_spec, mode, extraArgs = []) => { - capturedMode = mode; - capturedArgs = extraArgs; - return { - stdout: '120,240,1280,720', - stderr: 'focus=focused\nstate=visible', - }; - }, - }); - - assert.deepEqual(result, { +test('findWindowsMpvTargetWindowHandle prefers the focused mpv window', () => { + const result: MpvPollResult = { matches: [ { - hwnd: 0, - bounds: { - x: 120, - y: 240, - width: 1280, - height: 720, - }, + hwnd: 111, + bounds: { x: 0, y: 0, width: 1280, height: 720 }, area: 1280 * 720, + isForeground: false, + }, + { + hwnd: 222, + bounds: { x: 10, y: 10, width: 800, height: 600 }, + area: 800 * 600, isForeground: true, }, ], focusState: true, windowState: 'visible', - }); - assert.equal(capturedMode, 'geometry'); - assert.deepEqual(capturedArgs, ['\\\\.\\pipe\\subminer-socket']); + }; + + assert.equal(findWindowsMpvTargetWindowHandle(result), 222); }); -test('queryWindowsTargetWindowHandle resolves the selected hwnd from the powershell helper', () => { - let capturedMode: string | null = null; - let capturedArgs: string[] | null = null; - - const hwnd = queryWindowsTargetWindowHandle({ - targetMpvSocketPath: '\\\\.\\pipe\\subminer-socket', - resolveHelper: () => ({ - kind: 'powershell', - command: 'powershell.exe', - args: ['-File', 'helper.ps1'], - helperPath: 'helper.ps1', - }), - runHelperSync: (_spec, mode, extraArgs = []) => { - capturedMode = mode; - capturedArgs = extraArgs; - return { - stdout: '12345', - stderr: '', - }; - }, - }); - - assert.equal(hwnd, 12345); - assert.equal(capturedMode, 'target-hwnd'); - assert.deepEqual(capturedArgs, ['\\\\.\\pipe\\subminer-socket']); -}); - -test('shouldUseWindowsTrackerPowershellFallback returns true for explicit powershell mode', () => { - assert.equal( - windowsHelper.shouldUseWindowsTrackerPowershellFallback({ - helperModeEnv: 'powershell', - }), - true, - ); -}); - -test('shouldUseWindowsTrackerPowershellFallback returns true for ps1 helper path override', () => { - assert.equal( - windowsHelper.shouldUseWindowsTrackerPowershellFallback({ - helperPathEnv: 'C:\\custom\\tracker.ps1', - }), - true, - ); -}); - -test('shouldUseWindowsTrackerPowershellFallback returns false for default and native modes', () => { - assert.equal( - windowsHelper.shouldUseWindowsTrackerPowershellFallback({ - helperModeEnv: 'auto', - helperPathEnv: undefined, - }), - false, - ); - assert.equal( - windowsHelper.shouldUseWindowsTrackerPowershellFallback({ - helperModeEnv: 'native', - helperPathEnv: 'C:\\custom\\tracker.exe', - }), - false, - ); -}); - -test('resolveWindowsTrackerHelper auto mode prefers native helper when present', () => { - const helper = resolveWindowsTrackerHelper({ - dirname: 'C:\\repo\\dist\\window-trackers', - resourcesPath: 'C:\\repo\\resources', - existsSync: (candidate) => - candidate === 'C:\\repo\\resources\\scripts\\get-mpv-window-windows.exe', - helperModeEnv: 'auto', - }); - - assert.deepEqual(helper, { - kind: 'native', - command: 'C:\\repo\\resources\\scripts\\get-mpv-window-windows.exe', - args: [], - helperPath: 'C:\\repo\\resources\\scripts\\get-mpv-window-windows.exe', - }); -}); - -test('resolveWindowsTrackerHelper auto mode falls back to powershell helper', () => { - const helper = resolveWindowsTrackerHelper({ - dirname: 'C:\\repo\\dist\\window-trackers', - resourcesPath: 'C:\\repo\\resources', - existsSync: (candidate) => - candidate === 'C:\\repo\\resources\\scripts\\get-mpv-window-windows.ps1', - helperModeEnv: 'auto', - }); - - assert.deepEqual(helper, { - kind: 'powershell', - command: 'powershell.exe', - args: [ - '-NoProfile', - '-ExecutionPolicy', - 'Bypass', - '-File', - 'C:\\repo\\resources\\scripts\\get-mpv-window-windows.ps1', +test('findWindowsMpvTargetWindowHandle falls back to the largest visible mpv window', () => { + const result: MpvPollResult = { + matches: [ + { + hwnd: 111, + bounds: { x: 0, y: 0, width: 640, height: 360 }, + area: 640 * 360, + isForeground: false, + }, + { + hwnd: 222, + bounds: { x: 10, y: 10, width: 1920, height: 1080 }, + area: 1920 * 1080, + isForeground: false, + }, ], - helperPath: 'C:\\repo\\resources\\scripts\\get-mpv-window-windows.ps1', - }); + focusState: false, + windowState: 'visible', + }; + + assert.equal(findWindowsMpvTargetWindowHandle(result), 222); }); -test('resolveWindowsTrackerHelper explicit powershell mode ignores native helper', () => { - const helper = resolveWindowsTrackerHelper({ - dirname: 'C:\\repo\\dist\\window-trackers', - resourcesPath: 'C:\\repo\\resources', - existsSync: (candidate) => - candidate === 'C:\\repo\\resources\\scripts\\get-mpv-window-windows.exe' || - candidate === 'C:\\repo\\resources\\scripts\\get-mpv-window-windows.ps1', - helperModeEnv: 'powershell', - }); +test('findWindowsMpvTargetWindowHandle returns null when no mpv windows are visible', () => { + const result: MpvPollResult = { + matches: [], + focusState: false, + windowState: 'not-found', + }; - assert.equal(helper?.kind, 'powershell'); - assert.equal(helper?.helperPath, 'C:\\repo\\resources\\scripts\\get-mpv-window-windows.ps1'); -}); - -test('resolveWindowsTrackerHelper explicit native mode fails cleanly when helper is missing', () => { - const helper = resolveWindowsTrackerHelper({ - dirname: 'C:\\repo\\dist\\window-trackers', - resourcesPath: 'C:\\repo\\resources', - existsSync: () => false, - helperModeEnv: 'native', - }); - - assert.equal(helper, null); -}); - -test('resolveWindowsTrackerHelper explicit helper path overrides default search', () => { - const helper = resolveWindowsTrackerHelper({ - dirname: 'C:\\repo\\dist\\window-trackers', - resourcesPath: 'C:\\repo\\resources', - existsSync: (candidate) => candidate === 'D:\\custom\\tracker.ps1', - helperModeEnv: 'auto', - helperPathEnv: 'D:\\custom\\tracker.ps1', - }); - - assert.deepEqual(helper, { - kind: 'powershell', - command: 'powershell.exe', - args: ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', 'D:\\custom\\tracker.ps1'], - helperPath: 'D:\\custom\\tracker.ps1', - }); + assert.equal(findWindowsMpvTargetWindowHandle(result), null); }); diff --git a/src/window-trackers/windows-helper.ts b/src/window-trackers/windows-helper.ts index 856612a2..c71787ff 100644 --- a/src/window-trackers/windows-helper.ts +++ b/src/window-trackers/windows-helper.ts @@ -16,503 +16,41 @@ along with this program. If not, see . */ -import * as fs from 'node:fs'; -import * as os from 'node:os'; -import * as path from 'node:path'; -import { execFile, spawnSync, type ExecFileException } from 'child_process'; -import type { WindowGeometry } from '../types'; import type { MpvPollResult } from './win32'; -import { createLogger } from '../logger'; -const log = createLogger('tracker').child('windows-helper'); - -export type WindowsTrackerHelperKind = 'powershell' | 'native'; -export type WindowsTrackerHelperMode = 'auto' | 'powershell' | 'native'; -export type WindowsTrackerHelperRunMode = - | 'geometry' - | 'foreground-process' - | 'bind-overlay' - | 'lower-overlay' - | 'set-owner' - | 'clear-owner' - | 'target-hwnd'; - -export type WindowsTrackerHelperLaunchSpec = { - kind: WindowsTrackerHelperKind; - command: string; - args: string[]; - helperPath: string; -}; - -type ResolveWindowsTrackerHelperOptions = { - dirname?: string; - resourcesPath?: string; - helperModeEnv?: string | undefined; - helperPathEnv?: string | undefined; - existsSync?: (candidate: string) => boolean; - mkdirSync?: (candidate: string, options: { recursive: true }) => void; - copyFileSync?: (source: string, destination: string) => void; -}; - -const windowsPath = path.win32; - -function normalizeHelperMode(value: string | undefined): WindowsTrackerHelperMode { - const normalized = value?.trim().toLowerCase(); - if (normalized === 'powershell' || normalized === 'native') { - return normalized; - } - return 'auto'; +function loadWin32(): typeof import('./win32') { + return require('./win32') as typeof import('./win32'); } -export function shouldUseWindowsTrackerPowershellFallback(options: { - helperModeEnv?: string | undefined; - helperPathEnv?: string | undefined; -} = {}): boolean { - const mode = normalizeHelperMode( - options.helperModeEnv ?? process.env.SUBMINER_WINDOWS_TRACKER_HELPER, - ); - if (mode === 'powershell') { - return true; - } - - const helperPath = options.helperPathEnv ?? process.env.SUBMINER_WINDOWS_TRACKER_HELPER_PATH; - return helperPath?.trim().toLowerCase().endsWith('.ps1') ?? false; +export function findWindowsMpvTargetWindowHandle(result?: MpvPollResult): number | null { + const poll = result ?? loadWin32().findMpvWindows(); + const focused = poll.matches.find((match) => match.isForeground); + const best = + focused ?? [...poll.matches].sort((a, b) => b.area - a.area || b.bounds.width - a.bounds.width)[0]; + return best?.hwnd ?? null; } -function inferHelperKindFromPath(helperPath: string): WindowsTrackerHelperKind | null { - const normalized = helperPath.trim().toLowerCase(); - if (normalized.endsWith('.exe')) return 'native'; - if (normalized.endsWith('.ps1')) return 'powershell'; - return null; -} - -function materializeAsarHelper( - sourcePath: string, - kind: WindowsTrackerHelperKind, - deps: Required>, -): string | null { - if (!sourcePath.includes('.asar')) { - return sourcePath; - } - - const fileName = kind === 'native' ? 'get-mpv-window-windows.exe' : 'get-mpv-window-windows.ps1'; - const targetDir = windowsPath.join(os.tmpdir(), 'subminer', 'helpers'); - const targetPath = windowsPath.join(targetDir, fileName); - +export function setWindowsOverlayOwner(overlayHwnd: number, mpvHwnd: number): boolean { try { - deps.mkdirSync(targetDir, { recursive: true }); - deps.copyFileSync(sourcePath, targetPath); - log.info(`Materialized Windows helper from asar: ${targetPath}`); - return targetPath; - } catch (error) { - log.warn(`Failed to materialize Windows helper from asar: ${sourcePath}`, error); - return null; - } -} - -function createLaunchSpec( - helperPath: string, - kind: WindowsTrackerHelperKind, -): WindowsTrackerHelperLaunchSpec { - if (kind === 'native') { - return { - kind, - command: helperPath, - args: [], - helperPath, - }; - } - - return { - kind, - command: 'powershell.exe', - args: ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', helperPath], - helperPath, - }; -} - -function normalizeHelperPathOverride( - helperPathEnv: string | undefined, - mode: WindowsTrackerHelperMode, -): { path: string; kind: WindowsTrackerHelperKind } | null { - const helperPath = helperPathEnv?.trim(); - if (!helperPath) { - return null; - } - - const inferredKind = inferHelperKindFromPath(helperPath); - const kind = mode === 'auto' ? inferredKind : mode; - if (!kind) { - log.warn( - `Ignoring SUBMINER_WINDOWS_TRACKER_HELPER_PATH with unsupported extension: ${helperPath}`, - ); - return null; - } - - return { path: helperPath, kind }; -} - -function getHelperCandidates( - dirname: string, - resourcesPath: string | undefined, -): Array<{ - path: string; - kind: WindowsTrackerHelperKind; -}> { - const scriptFileBase = 'get-mpv-window-windows'; - const candidates: Array<{ path: string; kind: WindowsTrackerHelperKind }> = []; - - if (resourcesPath) { - candidates.push({ - path: windowsPath.join(resourcesPath, 'scripts', `${scriptFileBase}.exe`), - kind: 'native', - }); - candidates.push({ - path: windowsPath.join(resourcesPath, 'scripts', `${scriptFileBase}.ps1`), - kind: 'powershell', - }); - } - - candidates.push({ - path: windowsPath.join(dirname, '..', 'scripts', `${scriptFileBase}.exe`), - kind: 'native', - }); - candidates.push({ - path: windowsPath.join(dirname, '..', 'scripts', `${scriptFileBase}.ps1`), - kind: 'powershell', - }); - candidates.push({ - path: windowsPath.join(dirname, '..', '..', 'scripts', `${scriptFileBase}.exe`), - kind: 'native', - }); - candidates.push({ - path: windowsPath.join(dirname, '..', '..', 'scripts', `${scriptFileBase}.ps1`), - kind: 'powershell', - }); - - return candidates; -} - -export function parseWindowTrackerHelperOutput(output: string): WindowGeometry | null { - const result = output.trim(); - if (!result || result === 'not-found') { - return null; - } - - const parts = result.split(','); - if (parts.length !== 4) { - return null; - } - - const [xText, yText, widthText, heightText] = parts; - const x = Number.parseInt(xText!, 10); - const y = Number.parseInt(yText!, 10); - const width = Number.parseInt(widthText!, 10); - const height = Number.parseInt(heightText!, 10); - if ( - !Number.isFinite(x) || - !Number.isFinite(y) || - !Number.isFinite(width) || - !Number.isFinite(height) || - width <= 0 || - height <= 0 - ) { - return null; - } - - return { x, y, width, height }; -} - -export function parseWindowTrackerHelperFocusState(output: string): boolean | null { - const focusLine = output - .split(/\r?\n/) - .map((line) => line.trim()) - .find((line) => line.startsWith('focus=')); - - if (!focusLine) { - return null; - } - - const value = focusLine.slice('focus='.length).trim().toLowerCase(); - if (value === 'focused') { - return true; - } - if (value === 'not-focused') { - return false; - } - - return null; -} - -export function parseWindowTrackerHelperState(output: string): 'visible' | 'minimized' | null { - const stateLine = output - .split(/\r?\n/) - .map((line) => line.trim()) - .find((line) => line.startsWith('state=')); - - if (!stateLine) { - return null; - } - - const value = stateLine.slice('state='.length).trim().toLowerCase(); - if (value === 'visible') { - return 'visible'; - } - if (value === 'minimized') { - return 'minimized'; - } - - return null; -} - -export function parseWindowTrackerHelperForegroundProcess(output: string): string | null { - const processLine = output - .split(/\r?\n/) - .map((line) => line.trim()) - .find((line) => line.startsWith('process=')); - - if (!processLine) { - return null; - } - - const value = processLine.slice('process='.length).trim(); - return value.length > 0 ? value : null; -} - -type WindowsTrackerHelperRunnerResult = { - stdout: string; - stderr: string; -}; - -function runWindowsTrackerHelperWithSpawnSync( - spec: WindowsTrackerHelperLaunchSpec, - mode: WindowsTrackerHelperRunMode, - extraArgs: string[] = [], -): WindowsTrackerHelperRunnerResult | null { - const modeArgs = spec.kind === 'native' ? ['--mode', mode] : ['-Mode', mode]; - const result = spawnSync(spec.command, [...spec.args, ...modeArgs, ...extraArgs], { - encoding: 'utf-8', - timeout: 1000, - maxBuffer: 1024 * 1024, - windowsHide: true, - }); - - if (result.error || result.status !== 0) { - return null; - } - - return { - stdout: result.stdout ?? '', - stderr: result.stderr ?? '', - }; -} - -function runWindowsTrackerHelperWithExecFile( - spec: WindowsTrackerHelperLaunchSpec, - mode: WindowsTrackerHelperRunMode, - extraArgs: string[] = [], -): Promise { - return new Promise((resolve, reject) => { - const modeArgs = spec.kind === 'native' ? ['--mode', mode] : ['-Mode', mode]; - execFile( - spec.command, - [...spec.args, ...modeArgs, ...extraArgs], - { - encoding: 'utf-8', - timeout: 1000, - maxBuffer: 1024 * 1024, - windowsHide: true, - }, - (error: ExecFileException | null, stdout: string, stderr: string) => { - if (error) { - reject(Object.assign(error, { stderr })); - return; - } - resolve({ stdout, stderr }); - }, - ); - }); -} - -export async function queryWindowsForegroundProcessName(deps: { - resolveHelper?: () => WindowsTrackerHelperLaunchSpec | null; - runHelper?: ( - spec: WindowsTrackerHelperLaunchSpec, - mode: WindowsTrackerHelperRunMode, - extraArgs?: string[], - ) => Promise; -} = {}): Promise { - const spec = - deps.resolveHelper?.() ?? - resolveWindowsTrackerHelper({ - helperModeEnv: 'powershell', - }); - if (!spec || spec.kind !== 'powershell') { - return null; - } - - const runHelper = deps.runHelper ?? runWindowsTrackerHelperWithExecFile; - const { stdout } = await runHelper(spec, 'foreground-process'); - return parseWindowTrackerHelperForegroundProcess(stdout); -} - -export function queryWindowsTrackerMpvWindows(deps: { - targetMpvSocketPath?: string | null; - resolveHelper?: () => WindowsTrackerHelperLaunchSpec | null; - runHelperSync?: ( - spec: WindowsTrackerHelperLaunchSpec, - mode: WindowsTrackerHelperRunMode, - extraArgs?: string[], - ) => WindowsTrackerHelperRunnerResult | null; -} = {}): MpvPollResult | null { - const targetMpvSocketPath = deps.targetMpvSocketPath?.trim(); - if (!targetMpvSocketPath) { - return null; - } - - const spec = - deps.resolveHelper?.() ?? - resolveWindowsTrackerHelper({ - helperModeEnv: 'powershell', - }); - if (!spec || spec.kind !== 'powershell') { - return null; - } - - const runHelper = deps.runHelperSync ?? runWindowsTrackerHelperWithSpawnSync; - const result = runHelper(spec, 'geometry', [targetMpvSocketPath]); - if (!result) { - return null; - } - - const geometry = parseWindowTrackerHelperOutput(result.stdout); - if (!geometry) { - return { - matches: [], - focusState: parseWindowTrackerHelperFocusState(result.stderr) ?? false, - windowState: parseWindowTrackerHelperState(result.stderr) ?? 'not-found', - }; - } - - const focusState = parseWindowTrackerHelperFocusState(result.stderr) ?? false; - return { - matches: [ - { - hwnd: 0, - bounds: geometry, - area: geometry.width * geometry.height, - isForeground: focusState, - }, - ], - focusState, - windowState: parseWindowTrackerHelperState(result.stderr) ?? 'visible', - }; -} - -export function queryWindowsTargetWindowHandle(deps: { - targetMpvSocketPath?: string | null; - resolveHelper?: () => WindowsTrackerHelperLaunchSpec | null; - runHelperSync?: ( - spec: WindowsTrackerHelperLaunchSpec, - mode: WindowsTrackerHelperRunMode, - extraArgs?: string[], - ) => WindowsTrackerHelperRunnerResult | null; -} = {}): number | null { - const targetMpvSocketPath = deps.targetMpvSocketPath?.trim(); - if (!targetMpvSocketPath) { - return null; - } - - const spec = - deps.resolveHelper?.() ?? - resolveWindowsTrackerHelper({ - helperModeEnv: 'powershell', - }); - if (!spec || spec.kind !== 'powershell') { - return null; - } - - const runHelper = deps.runHelperSync ?? runWindowsTrackerHelperWithSpawnSync; - const result = runHelper(spec, 'target-hwnd', [targetMpvSocketPath]); - if (!result) { - return null; - } - - const handle = Number.parseInt(result.stdout.trim(), 10); - return Number.isFinite(handle) && handle > 0 ? handle : null; -} - -export async function syncWindowsOverlayToMpvZOrder(deps: { - overlayWindowHandle: string; - targetMpvSocketPath?: string | null; - resolveHelper?: () => WindowsTrackerHelperLaunchSpec | null; - runHelper?: ( - spec: WindowsTrackerHelperLaunchSpec, - mode: WindowsTrackerHelperRunMode, - extraArgs?: string[], - ) => Promise; -}): Promise { - const spec = - deps.resolveHelper?.() ?? - resolveWindowsTrackerHelper({ - helperModeEnv: 'powershell', - }); - if (!spec || spec.kind !== 'powershell') { - return false; - } - - const runHelper = deps.runHelper ?? runWindowsTrackerHelperWithExecFile; - const extraArgs = [deps.targetMpvSocketPath ?? '', deps.overlayWindowHandle]; - const { stdout } = await runHelper(spec, 'bind-overlay', extraArgs); - return stdout.trim() === 'ok'; -} - -export async function lowerWindowsOverlayInZOrder(deps: { - overlayWindowHandle: string; - resolveHelper?: () => WindowsTrackerHelperLaunchSpec | null; - runHelper?: ( - spec: WindowsTrackerHelperLaunchSpec, - mode: WindowsTrackerHelperRunMode, - extraArgs?: string[], - ) => Promise; -}): Promise { - const spec = - deps.resolveHelper?.() ?? - resolveWindowsTrackerHelper({ - helperModeEnv: 'powershell', - }); - if (!spec || spec.kind !== 'powershell') { - return false; - } - - const runHelper = deps.runHelper ?? runWindowsTrackerHelperWithExecFile; - const { stdout } = await runHelper(spec, 'lower-overlay', ['', deps.overlayWindowHandle]); - return stdout.trim() === 'ok'; -} - -export function setWindowsOverlayOwnerNative(overlayHwnd: number, mpvHwnd: number): boolean { - try { - const win32 = require('./win32') as typeof import('./win32'); - win32.setOverlayOwner(overlayHwnd, mpvHwnd); + loadWin32().setOverlayOwner(overlayHwnd, mpvHwnd); return true; } catch { return false; } } -export function ensureWindowsOverlayTransparencyNative(overlayHwnd: number): boolean { +export function ensureWindowsOverlayTransparency(overlayHwnd: number): boolean { try { - const win32 = require('./win32') as typeof import('./win32'); - win32.ensureOverlayTransparency(overlayHwnd); + loadWin32().ensureOverlayTransparency(overlayHwnd); return true; } catch { return false; } } -export function bindWindowsOverlayAboveMpvNative(overlayHwnd: number, mpvHwnd: number): boolean { +export function bindWindowsOverlayAboveMpv(overlayHwnd: number, mpvHwnd: number): boolean { try { - const win32 = require('./win32') as typeof import('./win32'); + const win32 = loadWin32(); win32.bindOverlayAboveMpv(overlayHwnd, mpvHwnd); win32.ensureOverlayTransparency(overlayHwnd); return true; @@ -521,85 +59,19 @@ export function bindWindowsOverlayAboveMpvNative(overlayHwnd: number, mpvHwnd: n } } -export function clearWindowsOverlayOwnerNative(overlayHwnd: number): boolean { +export function clearWindowsOverlayOwner(overlayHwnd: number): boolean { try { - const win32 = require('./win32') as typeof import('./win32'); - win32.clearOverlayOwner(overlayHwnd); + loadWin32().clearOverlayOwner(overlayHwnd); return true; } catch { return false; } } -export function getWindowsForegroundProcessNameNative(): string | null { +export function getWindowsForegroundProcessName(): string | null { try { - const win32 = require('./win32') as typeof import('./win32'); - return win32.getForegroundProcessName(); + return loadWin32().getForegroundProcessName(); } catch { return null; } } - -export function resolveWindowsTrackerHelper( - options: ResolveWindowsTrackerHelperOptions = {}, -): WindowsTrackerHelperLaunchSpec | null { - const existsSync = options.existsSync ?? fs.existsSync; - const mkdirSync = options.mkdirSync ?? fs.mkdirSync; - const copyFileSync = options.copyFileSync ?? fs.copyFileSync; - const dirname = options.dirname ?? __dirname; - const resourcesPath = options.resourcesPath ?? process.resourcesPath; - const mode = normalizeHelperMode( - options.helperModeEnv ?? process.env.SUBMINER_WINDOWS_TRACKER_HELPER, - ); - const override = normalizeHelperPathOverride( - options.helperPathEnv ?? process.env.SUBMINER_WINDOWS_TRACKER_HELPER_PATH, - mode, - ); - - if (override) { - if (!existsSync(override.path)) { - log.warn(`Configured Windows tracker helper path does not exist: ${override.path}`); - return null; - } - const helperPath = materializeAsarHelper(override.path, override.kind, { - mkdirSync, - copyFileSync, - }); - return helperPath ? createLaunchSpec(helperPath, override.kind) : null; - } - - const candidates = getHelperCandidates(dirname, resourcesPath); - const orderedCandidates = - mode === 'powershell' - ? candidates.filter((candidate) => candidate.kind === 'powershell') - : mode === 'native' - ? candidates.filter((candidate) => candidate.kind === 'native') - : candidates; - - for (const candidate of orderedCandidates) { - if (!existsSync(candidate.path)) { - continue; - } - - const helperPath = materializeAsarHelper(candidate.path, candidate.kind, { - mkdirSync, - copyFileSync, - }); - if (!helperPath) { - continue; - } - - log.info(`Using Windows helper (${candidate.kind}): ${helperPath}`); - return createLaunchSpec(helperPath, candidate.kind); - } - - if (mode === 'native') { - log.warn('Windows native tracker helper requested but no helper was found.'); - } else if (mode === 'powershell') { - log.warn('Windows PowerShell tracker helper requested but no helper was found.'); - } else { - log.warn('Windows tracker helper not found.'); - } - - return null; -} diff --git a/src/window-trackers/windows-tracker.ts b/src/window-trackers/windows-tracker.ts index 648d8de5..f7375799 100644 --- a/src/window-trackers/windows-tracker.ts +++ b/src/window-trackers/windows-tracker.ts @@ -19,10 +19,6 @@ import { BaseWindowTracker } from './base-tracker'; import type { WindowGeometry } from '../types'; import type { MpvPollResult } from './win32'; -import { - queryWindowsTrackerMpvWindows, - shouldUseWindowsTrackerPowershellFallback, -} from './windows-helper'; import { createLogger } from '../logger'; const log = createLogger('tracker').child('windows'); @@ -35,16 +31,8 @@ type WindowsTrackerDeps = { now?: () => number; }; -function defaultPollMpvWindows(targetMpvSocketPath?: string | null): MpvPollResult { - if (targetMpvSocketPath && shouldUseWindowsTrackerPowershellFallback()) { - const helperResult = queryWindowsTrackerMpvWindows({ - targetMpvSocketPath, - }); - if (helperResult) { - return helperResult; - } - } - +function defaultPollMpvWindows(_targetMpvSocketPath?: string | null): MpvPollResult { + void _targetMpvSocketPath; const win32 = require('./win32') as typeof import('./win32'); return win32.findMpvWindows(); }