Skip to content

Expose cursor position, color, and timing to custom pixel shaders #20704

Description

@pyerro

Description of the new feature

I would like to extend the experimental custom pixel shader constant buffer (PixelShaderSettings) with the current and previous cursor rectangle, their resolved colors, and the timestamp of the last cursor change.

With those values, an HLSL shader could draw time-based cursor movement animations—such as trails, smears, and warps—without trying to infer the cursor position from the composited frame.

The current constant buffer tells a shader when it is running and how large the viewport is, but provides no cursor state: where the cursor is, where it was before the last move, what color it is actually drawn in, or when the move occurred.

This information cannot be recovered reliably from the framebuffer. There is no cursor marker or stencil, so a shader would need to distinguish the cursor from glyphs, selection highlights, and reverse-video content. Cursor shape and auto-resolved color make that heuristic less reliable, while the lack of a history buffer prevents the shader from retaining the source rectangle of an in-flight move. Blinking also removes the cursor from the frame entirely, making it appear to have moved or vanished.

The relevant data already exists on the CPU side near where the constant buffer is populated. Many cursor-motion effects in other terminals depend on this state. Ghostty, for example, exposes it as iCurrentCursor, iPreviousCursor, iCurrentCursorColor, iPreviousCursorColor, and iTimeCursorChange. Without an equivalent, these effects cannot be ported faithfully using the current API.

I have a working local prototype against main (AtlasEngine / BackendD3D), along with demo shaders and a screen recording. I am opening this issue first to determine whether the team is interested in this API and its proposed shape before submitting a PR.

terminal-shader-demo.mp4

Proposed technical implementation details

Append the following fields to the existing constant buffer so that shaders declaring only the current fields remain compatible:

cbuffer PixelShaderSettings
{
    // existing
    float  Time;
    float  Scale;
    float2 Resolution;
    float4 Background;

    // proposed
    float4 CurrentCursor;        // x, y, width, height — in pixels
    float4 PreviousCursor;       // same, before the last cursor change
    float4 CurrentCursorColor;   // resolved, premultiplied RGBA
    float4 PreviousCursorColor;  // same, before the last cursor change
    float  TimeCursorChange;     // value of Time at the last cursor change
};

The cursor rectangles use viewport pixels, with the origin at the top-left and positive x/y extending right/down. This is the same coordinate space as SV_POSITION.xy and Resolution. xy contains the top-left corner and zw the width and height of the rectangle actually drawn, including adjustments for bar, underscore, doubleUnderscore, and vintage cursor shapes.

The colors contain the values resolved by the built-in cursor renderer, including the #ffffff auto-color path and its perceivability adjustment. TimeCursorChange uses the same clock and 1000-second wrap as Time.

Proposed behavior:

  • Append the fields without changing the behavior of existing shaders.
  • Seed the previous cursor state from the current state when the shader is created or recreated, preventing an initial animation from (0, 0).
  • Track cursor geometry independently of the blink phase, so blinking is not treated as movement.
  • Reset the transition after scrolling, resizing, or render-setting changes to avoid trails across coordinate-space jumps.

The local prototype adds approximately 115 lines across common.h, AtlasEngine.cpp, BackendD3D.h, and BackendD3D.cpp, with no behavior change to the built-in cursor. I also created a small debug shader and SDF-based sample effects under samples/PixelShaders/.

I manually tested all five cursor shapes, blinking, scrolling, resizing, explicit and automatic cursor colors, shader recreation, and the existing sample shaders.

This proposal is limited to the uniforms themselves and one small demonstration shader. General HLSL shader chaining, built-in cursor effects, new settings UI, and shipping Ghostty shader ports are out of scope.

Questions for the team:

  1. Is this kind of extension to the custom-shader contract acceptable?
  2. Is the proposed uniform set and pixel-based coordinate system the shape you would prefer?

The prototype was developed with AI-assisted tooling (Codex, Claude Code). I have reviewed, built, and manually tested the implementation.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-FeatureComplex enough to require an in depth planning process and actual budgeted, scheduled work.Needs-Tag-FixDoesn't match tag requirementsNeeds-TriageIt's a new issue that the core contributor team needs to triage at the next triage meeting

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions