You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Desktop 2.1.1: cursor confined to stale 1280x720 ClipCursor rect on multi-monitor while sessions process #57732
Windows confines the OS cursor to 0,0-1280,720 while sessions are being processed in OpenCode Desktop 2.1.1. This is the 2.x follow-up to #45171 (closed for the 2.0 re-test; I cannot reopen it because I am not the original author and have no triage rights).
With the watchdog disabled the rect stays applied and keeps coming back. Esc/Alt+Tab releases the cursor, but it re-arms shortly after. SendInput (absolute and relative) is ignored while clipped; only physical movement or SetCursorPos sometimes escapes. GetCapture is 0 and the window under the cursor is the OpenCode renderer.
The important detail is the geometry: the clip rect matches the app's hardcoded default window size (1280x800, i.e. a 1280x720 client area), not the current maximized size (2560x1440). That points at a pointer-lock/clip rect captured once at default geometry and never recomputed on resize/maximize.
Environment:
OpenCode 2.1.1 (Electron 44.4.5), Windows 11 build 26300
Primary 2560x1440 @100%, secondary 1920x1080 at x=-1920
IDE/browser panel open, session processing
Evidence (GetClipCursor, 100 ms polling, real capture):
07:58:11.139 clip=-1920,0-2560,1440 (free)
07:58:18.657 clip=0,0-1280,720 (applied by OpenCode)
07:58:18.777 clip=-1920,0-2560,1440 (released by watchdog, ~120 ms later)
Notes from inspecting the shipped 2.1.1 bundle (may save triage time):
ClipCursor, setIgnoreMouseEvents and requestPointerLock do not appear in app.asar application code (only inside bundled caniuse data). The clip is therefore applied by Chromium's native Windows layer (pointer lock / mouse capture), not by app JS.
The default 1280x800 is hardcoded in two places: packages/desktop/src/main/windows/index.ts and packages/desktop/src/main/windows/early.ts, both via resolveWindowState(..., { width: 1280, height: 800 }, ...).
packages/desktop/src/main/windows/window-state.ts persists bounds only while the window is not maximized, stores isMaximized separately, and restores by creating the window at the normal bounds and then calling win.maximize(). Its resize/move listeners only debounce a state write; nothing releases or recomputes a clip on resize, maximize, hide or close.
The bundle creates one WebContentsView for popup web content (sandboxed, webviewTag: false), which is the only embedded-web-content surface where a real pointer lock could be requested.
Specific questions:
Is the clip/pointer-lock rect recomputed when the window is resized or maximized?
Can the IDE/browser panel leave a stale lock behind after its session ends, or when the panel is hidden/closed?
Workaround: a background watchdog polls GetClipCursor at ~7 Hz and calls ClipCursor(NULL) whenever the rect does not cover the primary display.
Plugins
No response
OpenCode version
2.1.1 (Desktop, Electron 44.4.5)
Steps to reproduce
Windows 11, two displays (primary 2560x1440 @100%, secondary 1920x1080 at x=-1920).
Open OpenCode Desktop 2.1.1 with the IDE/browser panel open.
Start a session and let it process.
Poll GetClipCursor at 100 ms: the rect becomes 0,0-1280,720 and stays/reapplies until released externally.
Screenshot
GetClipCursor log excerpt is inline above. Full multi-hour log available on request.
Thanks for the detailed report and the GetClipCursor log. It's the right follow-up to #45171, which described the same symptom on v1.
We couldn't reproduce this here. Cursor clipping only happens with the Desktop app on Windows, and our test environment can't run that, so a maintainer with a Windows multi-monitor setup will need to look at it.
Some notes that may help with that:
Your reading of the window code is right. Nothing in the app calls ClipCursor or pointer lock directly, and the 1280x800 window default only affects the window's own size.
When the agent uses the browser panel, it sends synthetic mouse events (moves, clicks, drags) into the panel's embedded page. Hidden tabs in that panel also start at a fixed default size. We suspect Chromium may confine the cursor based on that embedded view rather than the maximized window, but we haven't confirmed it.
It would help to know:
Does the clip only appear when the agent is actually using the browser (navigating, clicking or dragging in the panel)? Or does it also happen in sessions where the agent never touches the browser?
Does it still happen with the IDE/browser panel closed?
Does it still happen on 2.1.2?
gilmarribeiropereira78-stack commented on Oct 21, 2026
Panel open, agent idle: the clip still arms. The agent does not need to navigate, click or drag in the browser for it to happen.
Panel closed: no clip. Removing the IDE/browser panel is what stops it.
2.1.2: not tested yet; I will report back after upgrading.
So the synthetic-mouse-event path is not required for the trigger. The correlation is with the panel merely being open, not with a gesture happening inside it.
One more data point from the shipped 2.1.1 bundle, in case it narrows things down. The panel creates a single WebContentsView, and the only non-corner setBounds call in the whole app.asar is:
The view is anchored at the origin with a fixed default size before it is handed to the embed manager. That is not 1280x720 exactly, so I am not claiming it is the observed rect, but it is the only origin-anchored embedded surface in the app. Later sizing happens inside embeds.create / oe.show, which is not visible in the minified bundle.
What would be worth checking on your side:
Is a clip applied when that view is created / attached / shown, before any navigation happens?
Is it released when the panel is hidden (visible=false) or the view is destroyed?
The other candidate geometry is still the one from the window code: 1280x720 is the client area of the hardcoded 1280x800 default window.
The GetClipCursor watchdog is still required to keep the machine usable on 2.1.1.
Activity
opencode-agent commentedon Oct 21, 2026
Thanks for the detailed report and the
GetClipCursorlog. It's the right follow-up to #45171, which described the same symptom on v1.We couldn't reproduce this here. Cursor clipping only happens with the Desktop app on Windows, and our test environment can't run that, so a maintainer with a Windows multi-monitor setup will need to look at it.
Some notes that may help with that:
ClipCursoror pointer lock directly, and the 1280x800 window default only affects the window's own size.It would help to know:
gilmarribeiropereira78-stack commentedon Oct 21, 2026
Tested the three questions on 2.1.1:
So the synthetic-mouse-event path is not required for the trigger. The correlation is with the panel merely being open, not with a gesture happening inside it.
One more data point from the shipped 2.1.1 bundle, in case it narrows things down. The panel creates a single
WebContentsView, and the only non-cornersetBoundscall in the wholeapp.asaris:The view is anchored at the origin with a fixed default size before it is handed to the embed manager. That is not
1280x720exactly, so I am not claiming it is the observed rect, but it is the only origin-anchored embedded surface in the app. Later sizing happens insideembeds.create/oe.show, which is not visible in the minified bundle.What would be worth checking on your side:
visible=false) or the view is destroyed?The other candidate geometry is still the one from the window code:
1280x720is the client area of the hardcoded1280x800default window.The
GetClipCursorwatchdog is still required to keep the machine usable on 2.1.1.