GuideMulti-monitorTerminator Monitor API

Can I use a TV as a computer monitor? Yes, and the piece every SERP result skips is what happens to your scripts.

Every top result for this query answers the cable question. HDMI 2.1, chroma subsampling at 4:4:4, refresh rate, input lag, burn-in, viewing distance. All correct, all consumer-side. The part nobody writes about is what the software on the PC side has to do once a TV becomes monitor 2. Any script, RPA flow, or AI agent driving the desktop has to become monitor-aware the moment your display count goes from one to two, and that is a solved problem sitting in a single file of the Terminator source.

T
Terminator
9 min read
4.9from Open-source, MIT
Monitor struct: 9 fields (id, name, is_primary, width, height, x, y, scale_factor, work_area)
desktop.get_monitor_by_name("LG TV") targets a display by its EDID product name
element.monitor() tells you which display any UI element is on, in one call
capture_monitor_by_id screenshots just the TV output at native 3840x2160

A 4K TV plus a laptop, by the numbers

These are the values Terminator's Monitor struct returns from a typical MacBook Pro with an LG C2 plugged in over HDMI. Different hardware shifts the numbers, but the ratio between displays is the cost that scripts pay.

0Fields on Monitor
0Laptop scale_factor
0TV scale_factor
0Pixels on a 4K TV (3840x2160)

The anchor fact: nine fields, one struct, verifiable in the repo

The entire TV-as-second-monitor story, from the software side, collapses into nine values. They live in crates/terminator/src/lib.rs starting at line 274. Clone the repo, grep for pub struct Monitor, and the definition is right there.

crates/terminator/src/lib.rs

The two fields that matter most for a TV-as-monitor setup are scale_factor (a 4K TV at 100% reports 0, a HiDPI laptop panel reports 0), and x / y, which can be negative if you mount the TV above your laptop. Every other TV-as-monitor article on the front page of Google stops at HDMI cables. None of them mention the per-display scale_factor split that trips up drag-and-drop scripts the first time.

The monitor-aware path, in five calls

Once a TV is plugged in, a reliable automation script follows this shape. Each step is a real method on the Terminator Desktop type.

From two displays to one correct click

1

1. list_monitors

Enumerate every display the OS can see

2

2. Match by name

get_monitor_by_name or filter the returned list

3

3. Inspect scale_factor

Expect 1.0 on most TVs, 1.5-2.0 on laptops

4

4. Translate coords

Offset your target by (monitor.x, monitor.y)

5

5. Capture or click

capture_monitor_by_id, or drive the window into tv.work_area

How Terminator resolves which display a window is on

role:Window
role:Window
role:Button
element.monitor()
Laptop panel
LG TV

What the Monitor API actually exposes

Every capability below lives in the open-source Terminator repo, implemented once per platform behind a shared trait in crates/terminator/src/platforms/mod.rs.

Nine fields per display

id, name, is_primary, width, height, x, y, scale_factor, work_area. Defined in crates/terminator/src/lib.rs:274. Enough to compute anything a coordinate-based script needs.

Name the TV, not its index

desktop.get_monitor_by_name("LG TV SSCR2") (lib.rs:712) targets by EDID product name. Survives unplugging and re-plugging, where index-based addressing does not.

Element-to-monitor is O(1)

Every UIElement has a .monitor() method (element.rs:1583). Ask which display a button is on before clicking it, instead of guessing from global coordinates.

Per-monitor screenshots

capture_monitor_by_id skips the compositing step across displays. Screenshot just the TV output for vision-model input, no crop math.

Work area, not full area

WorkAreaBounds (lib.rs:296) separates the taskbar from the usable rect on Windows. A TV used as display 2 usually has no taskbar, so work_area equals full area; a laptop rarely does.

Scale factor is per-display

A 4K TV at 100% scaling reports 1.0 while a laptop panel typically reports 1.5 or 2.0. Both values live on the same Desktop, and both are read fresh from the OS on every list_monitors call.

Read every display, every element's monitor

This is a slimmed-down version of examples/monitor_example.py in the Terminator source tree. Run it with a TV plugged in and you get two rows of Monitor output, one per display, with the exact numbers your script needs to click correctly.

examples/monitor_example.py
laptop + LG TV
MIT

Terminator is an open-source desktop automation framework. Every file path on this page is grep-able in a fresh clone of mediar-ai/terminator.

github.com/mediar-ai/terminator

Target the TV by name, not by index

Display indices shift every time the user unplugs and re-plugs things in a different order. Display names come from EDID and stay stable across reboots. Terminator lets you pick the one you mean.

tv-aware-window-move.ts

Treating TV-as-monitor naively vs monitor-aware

The left column is what most automation code actually does today. The right column is what Terminator ships.

FeatureNaive automationTerminator
Detecting a window is on the TVCompare window rect against a hardcoded resolutionelement.monitor() returns a Monitor whose name matches the TV
Scale factor mismatch between laptop and TVCoordinates drift, clicks land off-targetMonitor.scale_factor exposed per display (lib.rs line 288)
Negative-y TV above the laptopMath assumes (0, 0) is top-left of primaryMonitor.x and Monitor.y are i32, negatives supported
Screenshot just the TV outputFull-desktop screenshot, crop afterdesktop.capture_monitor_by_id(tv.id) (mod.rs line 164)
TV reports the wrong DPIHardcoded DPI constant, brittle per machineMonitor.scale_factor is read fresh from the OS per call
Taskbar on the laptop, full-bleed TVClick taskbar height offset by accidentMonitor.work_area exposes the non-taskbar region
TV sleeps and wakes mid-sessionStale monitor list until restartdesktop.list_monitors() refreshes on every call (lib.rs 638)

Why every SERP result stops at cables

The top ten results for this keyword are all written for a reader sitting in a living room with an HDMI cable in one hand. HP, PCWorld, TCL, Lenovo, Microcenter, Quora, PC Richard, EasyPC. They cover exactly the things that matter at that table: whether the cable fits, whether the picture looks right, whether gaming feels sluggish.

They do not cover the thing that matters the moment the TV is plugged in and the reader writes their first automation script, records their first RPA workflow, or points an AI agent at the desktop: per-display scale factors, negative-y origins, EDID product names, and per-element monitor attribution. Those four concepts are the day-two story for anyone running software that touches the screen.

Terminator is a developer framework for that day-two story, not a consumer app. It gives existing AI coding assistants the ability to control your whole OS (not just write code), which is exactly where the TV-as-monitor question stops being about cables.

Every TV brand shows up under a different name

Because the display name comes from EDID, the string your script matches against varies by manufacturer. These are common patterns that appear in Monitor.name.

LG TV SSCR2SAMSUNGSony BRAVIATCL 55C845Hisense U8KVizio V-SeriesPhilips 65OLEDPanasonic VIERASharp AQUOSRoku TVFire TV OmniGoogle TV Chromecast

The exact string your OS reports depends on driver, firmware, and connection type. Print Monitor.name once, then hard-code the match or use a substring check.

Want to try the Monitor API yourself?

Terminator is MIT-licensed and cross-platform. The Monitor struct, the get_monitor_by_name call, and the element.monitor() method are all in the same repo. Clone it, plug in a TV, and print the nine fields for every display you can see.

Open mediar-ai/terminator on GitHub

Frequently asked questions

Can I use any TV as a computer monitor?

Almost any modern TV works as a display, but the SERP gives the consumer answer and stops there. The software-side answer is that once you plug in a TV, your OS treats it as a second monitor with its own width, height, x and y offset, and scale_factor. On Windows this information is enumerated through EnumDisplayMonitors; on macOS through NSScreen and CGGetActiveDisplayList. Terminator wraps both behind a single Monitor struct defined at crates/terminator/src/lib.rs line 274. Any automation script that has to work on the TV-as-monitor configuration needs to read those values, not assume them.

Why does scale_factor matter when I plug a TV into a laptop?

A 4K TV at 100% scale reports scale_factor 1.0; a modern laptop panel typically reports 1.5 or 2.0 because it is a HiDPI display. If your script clicks at logical (100, 100), that is a physical (150, 150) on a laptop at 1.5x and a physical (100, 100) on the TV at 1.0x. Terminator exposes scale_factor per monitor (lib.rs:288), so the click-coordinate helper knows which scale to apply depending on which monitor the target element reports via element.monitor(). Without per-monitor scale tracking, drag-and-drop between displays lands in the wrong pixel.

How do I target just the TV from an automation script?

Terminator has Desktop::get_monitor_by_name at crates/terminator/src/lib.rs line 712. Pass the exact display name the OS reports for your TV: on Windows this is usually something like 'LG TV SSCR2' or 'SAMSUNG' pulled from EDID; on macOS it is the product name surfaced by CGDisplayCopyDisplayProductName. If you do not know the name, call desktop.list_monitors() first, print every Monitor.name, and pick yours. Index-based addressing (monitor 1 vs monitor 2) breaks when the user unplugs and re-plugs the TV in a different order; name-based addressing survives that.

What is the anchor_fact here that nobody else writes about?

Terminator's Monitor struct carries exactly nine fields: id, name, is_primary, width, height, x, y, scale_factor, and an optional work_area. You can verify this by reading crates/terminator/src/lib.rs lines 274 through 292 in the open-source repo. Every UIElement in the tree also has a .monitor() method (element.rs:1583) that returns which display it sits on. The combination means that once you have a Locator pointing at, say, a Chrome window, one call tells you whether that window is on your laptop panel or your TV and what the TV's scale_factor is. None of the TV-as-monitor articles on the first page of Google mention per-element monitor attribution, because the cable/refresh-rate framing does not invite it.

Does input lag on a TV break accessibility-API automation?

No, and this is a clean separation that is easy to miss. Input lag on a TV is the delay between the HDMI input signal and the pixels lighting up on the panel. Accessibility-API automation, which Terminator is built on, operates on the OS accessibility tree before pixels are rendered. So the TV's 30ms or 50ms input lag adds to what a human sees but adds nothing to how fast the script finds a button or sends a click. The script and the accessibility tree agree about the button's position the moment Windows UIA or macOS AX exposes it, regardless of when the panel finally shows it. Screenshot-based agents are the ones that pay the lag tax, because they wait for pixels.

How do coordinates map when the TV sits above the laptop?

Windows and macOS both allow negative coordinates for displays placed left, above, or off-primary of the primary display. If you arrange your TV above the laptop in Display Settings, the TV might report x=0, y=-2160 while the laptop reports x=0, y=0. Monitor.x and Monitor.y are typed as i32 in crates/terminator/src/lib.rs lines 285-286 precisely so they can represent negative offsets. A script that assumes (0, 0) is the top-left of the combined desktop will miss every click on the TV in that configuration. Reading the Monitor's origin before computing target coordinates is the fix.

Can Terminator screenshot just the TV, not the whole desktop?

Yes. crates/terminator/src/platforms/mod.rs line 164 defines capture_monitor_by_id, which takes a single monitor's id and returns only that display's pixels. This is different from a full-desktop screenshot cropped after the fact. The per-monitor capture path avoids one compositing pass and returns at the TV's native resolution (e.g. 3840x2160 for a 4K panel). For vision-model input where resolution and cost both matter, that distinction cuts the image size roughly in half compared to a combined-desktop snapshot.

What if the TV is the only display, no laptop panel at all?

Many living-room PCs run that way, and some CI machines connect a TV to a mini-PC as a stand-in for a real monitor. In that case the TV is Monitor.is_primary = true, the scale_factor is whatever Windows or macOS applies by default (1.0 for a 4K TV at 100%, 1.5 for 125%), and the taskbar ends up on the TV so Monitor.work_area becomes meaningful. Terminator also ships crates/terminator/src/platforms/windows/virtual_display.rs, a VirtualDisplayManager that lets you run UI automation on a Windows machine with no physical display at all. If the TV is off or disconnected, the headless path is still there.

Is the Monitor API the same across Windows and macOS?

The public API is identical. crates/terminator/src/platforms/mod.rs lines 149-165 declare the engine trait: list_monitors, get_primary_monitor, get_active_monitor, get_monitor_by_id, get_monitor_by_name, capture_monitor_by_id. Windows implements it against EnumDisplayMonitors and the xcap crate; macOS implements it against NSScreen and CGDirectDisplayID. The Monitor struct your code receives has the same shape on both. So a script that decides 'if the Chrome window is on a monitor named something containing TV, resize it to the TV's work_area' runs unchanged on a MacBook Pro and a Windows 11 desktop.

Terminator gives existing AI coding assistants the ability to control your whole desktop, not just write code. Like Playwright, but for every app on your OS.

terminatorDesktop automation SDK
© 2026 terminator. All rights reserved.