| --- |
| doc_id: codex-pip-reverse-engineering |
| title: "Codex picture-in-picture reverse engineering" |
| language: en |
| source_language: en |
| implementation_status: current |
| document_status: current |
| translation_status: source-only |
| last_verified: 2026-09-11 |
| owners: |
| - maka-backend |
| --- |
| <!-- |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| --> |
| |
| # Codex picture-in-picture reverse engineering |
| |
| What Maka's Computer Use mirror copies from Codex, where each fact came from, |
| and the four places Maka deliberately does something else. It keeps confirmed |
| native facts separate from implementation inference, the same way |
| [computer-use-cursor-provenance.md](./computer-use-cursor-provenance.md) |
| does. |
| |
| ## Inspected artifacts |
| |
| Two, and the split between them matters more than either one. |
| |
| | | path | holds | |
| |---|---|---| |
| | service | `~/.codex/computer-use/Codex Computer Use.app/Contents/MacOS/SkyComputerUseService` | the capture and publish half | |
| | host | `/Applications/ChatGPT.app/Contents/Resources/native/sky.node` | the window half | |
| | main JS | `/Applications/ChatGPT.app/Contents/Resources/app.asar` → `.vite/build/main-Be_0DBuv.js` | host registration | |
| |
| The service has `RemoteHostedPIPContentPublisher`, |
| `RemoteHostedPIPCaptureStream` and `CUAServiceRemoteHostedPIPController`, links |
| AVFoundation and ScreenCaptureKit, and uses `AVSampleBufferDisplayLayer` — but |
| does **not** link AVKit. It captures and publishes over XPC; it does not host a |
| window. |
| |
| The window lives in ChatGPT's own native addon, `sky.node`, in the `PIPStack*` |
| family. Searching only the service for a window symbol and concluding it does |
| not exist is a mistake this document exists partly to prevent: `RemoteHosted` in |
| a class name says the other half is in another process, so find that process |
| before concluding anything is absent. |
| |
| ## Confirmed: the panel |
| |
| From `RemoteHostedPIPContentCreateStackPanel`, read out of the disassembly |
| instruction by instruction: |
| |
| ```objc |
| NSPanel initWithContentRect:… styleMask:0x80 // NSWindowStyleMaskNonactivatingPanel |
| backing:2 defer:NO |
| setTitle: |
| setBackgroundColor: [NSColor clearColor] |
| setOpaque: NO |
| setHasShadow: NO |
| setLevel: 0 // NSNormalWindowLevel — deliberately not floating |
| setAcceptsMouseMovedEvents: YES |
| setCollectionBehavior: 0x108 // FullScreenAuxiliary | Transient |
| setHidesOnDeactivate: NO |
| setMovableByWindowBackground: NO // dragging is hand-rolled, see below |
| ``` |
| |
| and then `addChildWindow:ordered:`, from |
| `-[PIPStackWindow attachToOwnerWindowForPositioning]`. |
| |
| The two facts that carry the whole design are `setLevel: 0` and |
| `addChildWindow:`. A child window is ordered against its parent rather than |
| against the desktop, and is carried by its parent inside one window-server |
| transaction. |
| |
| ## Confirmed: the structures |
| |
| ``` |
| PIPStackHost { hostID, ownerWindow, anchorContentRect, anchors[], presentationScope } |
| PIPStackHostAnchor{ contentPoint, alignment } |
| PIPStackWindow attachToHost: / startFollowingOwnerWindow / ownerWindowFrameMayHaveChanged: |
| PIPStackController |
| drag beginDragAtContentPoint: → dragToContentPoint: → endDrag |
| snap nearestTargetAnchorForCurrentAnchor:draggingVelocity: → moveStackToAnchorAlongCurve: |
| motion configureMotionSpringsWithLeadItem:dragging:programmaticMove: |
| hosts moveStackToHostID: / compatibleAnchorsIncludingDragHosts: |
| clamp clampEnvelopeToVisibleScreen: |
| PIPStackItemMotion{ springStiffness, springDamping, velocity, target, origin, restOffset } |
| PIPStackContentView |
| reinstallMouseEventMonitors → addGlobalMonitorForEventsMatchingMask:handler: |
| addLocalMonitorForEventsMatchingMask:handler: |
| updateHoverFromCurrentMouseLocation / contentPointForCurrentMouseLocation |
| ``` |
| |
| `ownerWindowFrameMayHaveChanged:` reads `notification.object` and compares |
| `notification.name`, so it is a notification handler for the owner window's |
| move/resize. It recomputes the anchor. It does not move the window — the window |
| server does that, because the panel is a child. |
| |
| ## Confirmed: the constants |
| |
| **Springs**, from `configureMotionSpringsWithLeadItem:dragging:programmaticMove:` |
| — one `cmp w26, #0` followed by four `fcsel`, so two columns of four: |
| |
| | | not dragging | dragging | |
| |---|---|---| |
| | lead stiffness | 320 | 900 | |
| | lead damping | 42 | 55 | |
| | follower stiffness base | 150 | 260 | |
| | follower damping base | 30 | 32 | |
| |
| with the follower values divided by `1 + 0.18·s` and `1 + 0.08·s` where |
| `s = |index| · (1 + 0.45·|index|)`. |
| |
| The damping ratios are the reason those numbers and not others: settling is |
| ζ ≈ 1.17, just overdamped, so it never bounces past the corner it lands on; |
| dragging is ζ ≈ 0.92, just under critical, so the window keeps a trace of give |
| while the pointer pulls it. |
| |
| **Throw**, from `nearestTargetAnchorForCurrentAnchor:draggingVelocity:`: |
| |
| ``` |
| throw = velocity * 0.55 |
| t = min(|throw| / 5000, 0.45) |
| target = currentAnchor + unit(throw) * t |
| score(a)= |a.point − target| − |throw| · max(0, dot(unit(throw), unit(a.point − current))) |
| pick min score |
| ``` |
| |
| and separately `hypot(velocity) >= 120` decides whether anchors on other hosts |
| are candidates at all. |
| |
| The 0.55 is the difference between "the window goes where you threw it" and |
| "the window goes where you were pointing", and only the first feels like it has |
| weight. The dot-product term is what makes a deliberate throw across the window |
| land where it was aimed; distance alone picks the corner you are leaving. |
| |
| **Size**: default longest edge 200pt, clamped to [100, 400], aspect preserved by |
| scaling to the shorter edge. Anchor inset 24pt. The three bounds are the |
| literal doubles `0x4069…`, `0x4059…`, `0x4079…`. |
| |
| **Resize**, from `-[PIPStackResizeInteraction maxDisplaySizeForPointerScreenPoint:]`, |
| which is four instructions: |
| |
| ``` |
| sign = (alignment & ~1) == 2 ? +1 : -1 |
| size = initialMaxDisplaySize + (pointer.y - initialPointer.y) * sign |
| ``` |
| |
| Only the vertical component moves it, and the sign comes from the corner the |
| mirror rests on, so the gesture reads the same everywhere: away from the anchor |
| grows it. Nothing is incremental — the interaction keeps the edge and pointer |
| height it started from — so a jittery pointer cannot accumulate drift. |
| |
| **Controls**: `performControlWithIdentifier:` takes `stop`, `hide`, `close`; |
| `setHoveredControlIdentifier:` and `_pressedControlIdentifier` drive their |
| appearance. |
| |
| ## Measured on Electron 43, before writing any of it |
| |
| | | result | |
| |---|---| |
| | `documentPictureInPicture` | `undefined`, with and without `--enable-features=DocumentPictureInPictureAPI` | |
| | child window, parent moves | child follows, no code, same transaction | |
| | child window, parent resizes | child does not move — the anchor needs recomputing | |
| | child window `isAlwaysOnTop()` | `false` | |
| | child window focus | never taken | |
| | injected mouse events into a click-through window | delivered, but coalesced — 2 of 5, then 1 of 5 | |
| |
| Document Picture-in-Picture is not merely disabled in Electron; its |
| implementation lives in Chromium's `//chrome` browser layer |
| (`PictureInPictureWindowManager`), which Electron does not have. That road is |
| closed, not narrow. |
| |
| ## What Maka copies |
| |
| - Child window of the app window, at normal level. Both of the mirror's original |
| faults — floating above unrelated apps, and trailing a frame behind during a |
| drag — were one cause: positioning itself instead of being carried. |
| - `hasShadow: false`. `pip.html` draws its own; the native shadow would be a |
| second one, recomputed by the window server from the content's alpha every |
| time a frame lands, on the one window that receives a frame after every action. |
| - 200pt default edge, [100, 400] clamp, 24pt inset. |
| - The spring constants, the throw projection and the anchor scoring, in |
| `apps/desktop/src/main/computer-use/pip-motion.ts`, with the disassembly |
| quoted next to each constant. |
| - Hover decided from the pointer on the main side, because that is what Codex's |
| global and local `NSEvent` monitors do. It never asks the window whether the |
| pointer is inside it. |
| - Only resizes are acted on. A move is the window server's job. |
| |
| ## Where Maka does something else, and why |
| |
| **Two controls, not three.** Codex's `close` dismisses one tile and `hide` |
| dismisses the stack. Maka mirrors one window at a time, so those are the same |
| gesture and only one of them earns a button. |
| |
| **Click-through until pointed at.** Codex's tile always takes the click: |
| `acceptsFirstMouse:` returns YES and `hitTest:` claims the whole view. Its tile |
| is opt-in behind a setting worded "Show backgrounded apps that Computer Use is |
| working on in Picture-in-Picture mode"; Maka's appears whenever a run starts, so |
| it has to cost nothing to ignore. `setIgnoreMouseEvents(true, {forward: true})` |
| still delivers moves, so main can tell when to take the clicks back. |
| |
| **No stack.** Codex mirrors several tiles with a lead item and followers; the |
| follower spring column and its index falloff have nothing to apply to here. |
| |
| **The grip appears with the controls.** Codex's resize handles are part of the |
| same hover chrome; at rest the tile carries none. Same here — a 200pt window |
| cannot afford permanent affordances. |
| |
| **Frames, not video.** Codex streams via ScreenCaptureKit across XPC. Every |
| mutating Computer Use action already returns a screenshot of the target, |
| captured after the settle wait, so the mirror is a flipbook of post-action |
| frames and costs no extra capture. The trade is real and deliberate: between |
| actions, the mirror does not update. |
| |
| **No cross-host move.** `moveStackToHostID:` moves the stack between *owner |
| windows* — Codex registers several hosts, including `avatar-overlay`. Maka has |
| one window, so there is nothing to move between. Cross-display works because the |
| mirror is the app window's child and goes wherever that window goes. |
| |
| ## Verification |
| |
| `scripts/pip-interaction-smoke.mjs` runs the real thing: a real parent window, a |
| real child panel, the real preload and renderer, real input events. It asserts |
| the child-window properties, the seat position, the carry-on-move, hover, a |
| throw settling on the anchor it was aimed at, an app resize returning the mirror |
| to the chosen corner rather than the default one, a grip drag growing it |
| (measured 200x160 → 290x232, aspect held, still on its corner, inside the |
| clamp), and the hide control. |
| |
| It needs no accessibility and no unlocked screen — it drives Electron windows |
| only — which makes it the one real-machine check that keeps working when the |
| rest cannot run. |
| |
| The physics and the anchor scoring were covered without a desktop in |
| `apps/desktop/src/main/__tests__/computer-use-pip-motion.test.ts`; #2478 |
| deleted that test, which only ends the coverage it provided — nothing has |
| taken its place. The module under test is untouched: `pip-motion.ts` is |
| byte-identical across the #3293 cursor replacement (implemented in #3456), |
| which rebuilt the agent cursor engine and its glyphs — including the PiP |
| glyph — without replacing the PiP window's motion constants. The dragging |
| and settling springs and the throw factors transcribed above are still |
| defined there and still drive `pip-window.ts`. Cursor provenance is tracked |
| in computer-use-cursor-provenance.md, and it concerns the agent cursor only; |
| the PiP window's motion model is a separate surface with no provenance |
| change to record. |