• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

orion-ecs / keen-eye / 20819866367

08 Jan 2026 02:16PM UTC coverage: 87.309% (-0.01%) from 87.323%
20819866367

push

github

tyevco
feat(testbridge): Add region screenshot capture

Add capability to capture a specific region of the screen instead of the
full framebuffer. This is useful for focusing on specific UI elements
or game areas when debugging or testing.

Changes:
- Add CaptureRegionAsync, GetRegionScreenshotBytesAsync, and
  SaveRegionScreenshotAsync to ICaptureController interface
- Implement region capture with OpenGL coordinate conversion
  (screen top-left origin to GL bottom-left origin)
- Add IPC command handlers for capture.captureRegion,
  capture.getRegionScreenshotBytes, and capture.saveRegionScreenshot
- Add MCP tools: capture_screenshot_region and
  capture_screenshot_region_to_file
- Add 13 unit tests for region capture validation and encoding

Closes #854

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

9165 of 12271 branches covered (74.69%)

Branch coverage included in aggregate %.

99 of 157 new or added lines in 5 files covered. (63.06%)

9 existing lines in 3 files now uncovered.

158549 of 179822 relevant lines covered (88.17%)

1.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

9.8
/src/KeenEyes.TestBridge.Client/RemoteCaptureController.cs
1
using KeenEyes.TestBridge.Capture;
2
using KeenEyes.TestBridge.Ipc.Protocol;
3

4
namespace KeenEyes.TestBridge.Client;
5

6
/// <summary>
7
/// Remote implementation of <see cref="ICaptureController"/> that communicates over IPC.
8
/// </summary>
9
internal sealed class RemoteCaptureController(TestBridgeClient client) : ICaptureController
1✔
10
{
11
    /// <inheritdoc />
12
    public bool IsAvailable => client.SendRequestAsync<bool>("capture.isAvailable", null, CancellationToken.None)
1✔
13
        .GetAwaiter().GetResult();
1✔
14

15
    /// <inheritdoc />
16
    public bool IsRecording => client.SendRequestAsync<bool>("capture.isRecording", null, CancellationToken.None)
1✔
17
        .GetAwaiter().GetResult();
1✔
18

19
    /// <inheritdoc />
20
    public int RecordedFrameCount => client.SendRequestAsync<int>("capture.recordedFrameCount", null, CancellationToken.None)
×
21
        .GetAwaiter().GetResult();
×
22

23
    /// <inheritdoc />
24
    public async Task<FrameCapture> CaptureFrameAsync()
25
    {
26
        var result = await client.SendRequestAsync<FrameCapture>("capture.captureFrame", null, CancellationToken.None);
×
27
        return result;
×
28
    }
×
29

30
    /// <inheritdoc />
31
    public async Task<string> SaveScreenshotAsync(string filePath, ImageFormat format = ImageFormat.Png)
32
    {
33
        var args = new SaveScreenshotArgs { FilePath = filePath, Format = format.ToString() };
×
34
        var result = await client.SendRequestAsync<string>("capture.saveScreenshot", args, CancellationToken.None);
×
35
        return result ?? throw new InvalidOperationException("Failed to save screenshot");
×
36
    }
×
37

38
    /// <inheritdoc />
39
    public async Task<byte[]> GetScreenshotBytesAsync(ImageFormat format = ImageFormat.Png)
40
    {
41
        // Server returns base64-encoded bytes
42
        var args = new GetScreenshotBytesArgs { Format = format.ToString() };
×
43
        var base64 = await client.SendRequestAsync<string>("capture.getScreenshotBytes", args, CancellationToken.None);
×
44
        if (string.IsNullOrEmpty(base64))
×
45
        {
46
            throw new InvalidOperationException("Failed to get screenshot bytes");
×
47
        }
48

49
        return Convert.FromBase64String(base64);
×
50
    }
×
51

52
    /// <inheritdoc />
53
    public async Task<(int Width, int Height)> GetFrameSizeAsync()
54
    {
55
        var result = await client.SendRequestAsync<FrameSizeResult>("capture.getFrameSize", null, CancellationToken.None);
×
56
        if (result == null)
×
57
        {
58
            throw new InvalidOperationException("Failed to get frame size");
×
59
        }
60

61
        return (result.Width, result.Height);
×
62
    }
×
63

64
    /// <inheritdoc />
65
    public async Task<FrameCapture> CaptureRegionAsync(int x, int y, int width, int height)
66
    {
NEW
67
        var args = new CaptureRegionArgs { X = x, Y = y, Width = width, Height = height };
×
NEW
68
        var result = await client.SendRequestAsync<FrameCapture>("capture.captureRegion", args, CancellationToken.None);
×
NEW
69
        return result;
×
NEW
70
    }
×
71

72
    /// <inheritdoc />
73
    public async Task<byte[]> GetRegionScreenshotBytesAsync(int x, int y, int width, int height, ImageFormat format = ImageFormat.Png)
74
    {
NEW
75
        var args = new GetRegionScreenshotBytesArgs { X = x, Y = y, Width = width, Height = height, Format = format.ToString() };
×
NEW
76
        var base64 = await client.SendRequestAsync<string>("capture.getRegionScreenshotBytes", args, CancellationToken.None);
×
NEW
77
        if (string.IsNullOrEmpty(base64))
×
78
        {
NEW
79
            throw new InvalidOperationException("Failed to get region screenshot bytes");
×
80
        }
81

NEW
82
        return Convert.FromBase64String(base64);
×
NEW
83
    }
×
84

85
    /// <inheritdoc />
86
    public async Task<string> SaveRegionScreenshotAsync(int x, int y, int width, int height, string filePath, ImageFormat format = ImageFormat.Png)
87
    {
NEW
88
        var args = new SaveRegionScreenshotArgs { X = x, Y = y, Width = width, Height = height, FilePath = filePath, Format = format.ToString() };
×
NEW
89
        var result = await client.SendRequestAsync<string>("capture.saveRegionScreenshot", args, CancellationToken.None);
×
NEW
90
        return result ?? throw new InvalidOperationException("Failed to save region screenshot");
×
NEW
91
    }
×
92

93
    /// <inheritdoc />
94
    public async Task StartRecordingAsync(int maxFrames = 300)
95
    {
96
        var args = new StartRecordingArgs { MaxFrames = maxFrames };
×
97
        await client.SendRequestAsync("capture.startRecording", args, CancellationToken.None);
×
98
    }
×
99

100
    /// <inheritdoc />
101
    public async Task<IReadOnlyList<FrameCapture>> StopRecordingAsync()
102
    {
103
        var result = await client.SendRequestAsync<FrameCapture[]>("capture.stopRecording", null, CancellationToken.None);
×
104
        return result ?? [];
×
105
    }
×
106
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc