Skip to main content
Glama
windows-capture-engine.ts•10.5 kB
/** * Windows capture engine implementation */ import { exec } from "child_process"; import { promisify } from "util"; import { BaseCaptureEngine } from "./base-capture-engine"; import { DisplayInfo, WindowInfo } from "../types"; import { CaptureFailedError, DisplayNotFoundError, WindowNotFoundError, } from "../errors"; const execAsync = promisify(exec); /** * Windows-specific capture engine */ export class WindowsCaptureEngine extends BaseCaptureEngine { /** * Capture full screen or specific display */ async captureScreen(displayId?: string): Promise<Buffer> { try { // Use screenshot-desktop library for Windows const screenshot = await import("screenshot-desktop"); // If displayId is provided, capture that specific display const options: any = { format: "png" }; if (displayId) { options.screen = parseInt(displayId, 10); } const buffer = await screenshot.default(options); if (!buffer || buffer.length === 0) { throw new CaptureFailedError( "Screenshot capture returned empty buffer (headless environment?)", { displayId } ); } return buffer; } catch (error) { throw new CaptureFailedError( `Failed to capture screen: ${(error as Error).message}`, { displayId, error } ); } } /** * Capture a specific window */ async captureWindow( windowId: string, includeFrame: boolean ): Promise<Buffer> { // Validate window exists and is visible const window = await this.getWindowById(windowId); if (!window) { throw new WindowNotFoundError(`Window ${windowId} not found`, { windowId, }); } if (window.isMinimized) { throw new WindowNotFoundError( `Cannot capture minimized window ${windowId}`, { windowId, isMinimized: true } ); } try { // Use PowerShell to capture window // This is a simplified implementation - in production, you'd use native Windows APIs const script = ` Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing $hwnd = [int]${windowId} $rect = New-Object RECT [Win32]::GetWindowRect($hwnd, [ref]$rect) $width = $rect.Right - $rect.Left $height = $rect.Bottom - $rect.Top $bitmap = New-Object System.Drawing.Bitmap($width, $height) $graphics = [System.Drawing.Graphics]::FromImage($bitmap) $graphics.CopyFromScreen($rect.Left, $rect.Top, 0, 0, $bitmap.Size) $ms = New-Object System.IO.MemoryStream $bitmap.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png) [Convert]::ToBase64String($ms.ToArray()) `; const { stdout } = await execAsync( `powershell -Command "${script.replace(/"/g, '\\"')}"`, { maxBuffer: 50 * 1024 * 1024, } ); const buffer = Buffer.from(stdout.trim(), "base64"); if (!buffer || buffer.length === 0) throw new CaptureFailedError("Empty buffer from PowerShell", {}); return buffer; } catch (error) { throw new WindowNotFoundError( `Window ${windowId} not found or not capturable`, { windowId, error } ); } } /** * Capture a specific region (internal implementation) */ protected async captureRegionInternal( x: number, y: number, width: number, height: number ): Promise<Buffer> { try { // Use PowerShell to capture region const script = ` Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing $bitmap = New-Object System.Drawing.Bitmap(${width}, ${height}) $graphics = [System.Drawing.Graphics]::FromImage($bitmap) $graphics.CopyFromScreen(${x}, ${y}, 0, 0, $bitmap.Size) $ms = New-Object System.IO.MemoryStream $bitmap.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png) [Convert]::ToBase64String($ms.ToArray()) `; const { stdout } = await execAsync( `powershell -Command "${script.replace(/"/g, '\\"')}"`, { maxBuffer: 50 * 1024 * 1024, } ); const buffer = Buffer.from(stdout.trim(), "base64"); if (!buffer || buffer.length === 0) throw new CaptureFailedError("Empty buffer from PowerShell", {}); return buffer; } catch (error) { throw new CaptureFailedError( `Failed to capture region: ${(error as Error).message}`, { x, y, width, height, error } ); } } /** * Get all available displays */ async getDisplays(): Promise<DisplayInfo[]> { try { // Use PowerShell to get display information const script = ` Add-Type -AssemblyName System.Windows.Forms $screens = [System.Windows.Forms.Screen]::AllScreens $screens | ForEach-Object { $isPrimary = $_.Primary $bounds = $_.Bounds "$($_.DeviceName)|$isPrimary|$($bounds.Width)|$($bounds.Height)|$($bounds.X)|$($bounds.Y)" } `; const { stdout } = await execAsync( `powershell -Command "${script.replace(/"/g, '\\"')}"` ); const displays: DisplayInfo[] = []; const lines = stdout.trim().split("\n"); lines.forEach((line, index) => { const parts = line.trim().split("|"); if (parts.length === 6) { displays.push({ id: index.toString(), name: parts[0], resolution: { width: parseInt(parts[2], 10), height: parseInt(parts[3], 10), }, position: { x: parseInt(parts[4], 10), y: parseInt(parts[5], 10), }, isPrimary: parts[1].toLowerCase() === "true", }); } }); // Fallback if no displays found if (displays.length === 0) { displays.push({ id: "0", name: "Primary Display", resolution: { width: 1920, height: 1080 }, position: { x: 0, y: 0 }, isPrimary: true, }); } return displays; } catch (error) { // Fallback to basic display info return [ { id: "0", name: "Primary Display", resolution: { width: 1920, height: 1080 }, position: { x: 0, y: 0 }, isPrimary: true, }, ]; } } /** * Get all visible windows */ async getWindows(): Promise<WindowInfo[]> { try { // Use PowerShell to get window information const script = ` Add-Type @" using System; using System.Runtime.InteropServices; using System.Text; public class Win32 { [DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam); [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd); [DllImport("user32.dll")] public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); [DllImport("user32.dll")] public static extern int GetWindowTextLength(IntPtr hWnd); [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); } public struct RECT { public int Left; public int Top; public int Right; public int Bottom; } "@ $windows = @() $callback = { param($hwnd, $lParam) if ([Win32]::IsWindowVisible($hwnd)) { $length = [Win32]::GetWindowTextLength($hwnd) if ($length -gt 0) { $sb = New-Object System.Text.StringBuilder($length + 1) [Win32]::GetWindowText($hwnd, $sb, $sb.Capacity) | Out-Null $title = $sb.ToString() $rect = New-Object RECT [Win32]::GetWindowRect($hwnd, [ref]$rect) | Out-Null $pid = 0 [Win32]::GetWindowThreadProcessId($hwnd, [ref]$pid) | Out-Null $proc = Get-Process -Id $pid -ErrorAction SilentlyContinue $procName = if ($proc) { $proc.ProcessName } else { "unknown" } $width = $rect.Right - $rect.Left $height = $rect.Bottom - $rect.Top if ($width -gt 0 -and $height -gt 0) { "$hwnd|$title|$procName|$pid|$($rect.Left)|$($rect.Top)|$width|$height" } } } return $true } [Win32]::EnumWindows($callback, [IntPtr]::Zero) `; const { stdout } = await execAsync( `powershell -Command "${script.replace(/"/g, '\\"')}"`, { maxBuffer: 10 * 1024 * 1024, } ); const windows: WindowInfo[] = []; const lines = stdout.trim().split("\n"); for (const line of lines) { const parts = line.trim().split("|"); if (parts.length === 8) { windows.push({ id: parts[0], title: parts[1], processName: parts[2], pid: parseInt(parts[3], 10), bounds: { x: parseInt(parts[4], 10), y: parseInt(parts[5], 10), width: parseInt(parts[6], 10), height: parseInt(parts[7], 10), }, isMinimized: false, }); } } return windows; } catch (error) { // Return empty array if PowerShell fails return []; } } /** * Get a window by its ID */ async getWindowById(windowId: string): Promise<WindowInfo | null> { const windows = await this.getWindows(); return windows.find((w) => w.id === windowId) || null; } /** * Get a window by title pattern */ async getWindowByTitle(titlePattern: string): Promise<WindowInfo | null> { const windows = await this.getWindows(); const regex = new RegExp(titlePattern, "i"); return windows.find((w) => regex.test(w.title)) || null; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Digital-Defiance/mcp-screenshot'

If you have feedback or need assistance with the MCP directory API, please join our Discord server