How Alacritty Controls Cursor Styles and Blinking Behavior: A Technical Deep Dive

Alacritty controls cursor styles and blinking behavior through a three-stage pipeline that parses configuration from alacritty.yml, resolves the effective cursor style at runtime in Processor::update_cursor_blinking, and renders GPU-accelerated geometry via the display module.

The alacritty/alacritty repository implements cursor management as a coordinated system between user preferences, terminal state, and event-driven animations. Understanding how cursor styles and blinking behavior are controlled requires examining the configuration structs, the runtime decision logic in the event processor, and the final rendering path that generates visible cursor rectangles.

Configuration Model and User Settings

Cursor customization begins in alacritty/src/config/cursor.rs, where the Cursor struct defines all user-configurable properties. These values are deserialized from alacritty.yml into the running application's UiConfig.

Core Configuration Fields

The Cursor struct (lines 18-36) exposes these primary fields:

  • style: A ConfigCursorStyle defining the default shape and blinking mode (default: Block)
  • vi_mode_style: An optional override applied when the terminal enters VI mode
  • unfocused_hollow: Boolean flag rendering a hollow block when the window loses focus (default: true)
  • thickness: A percentage (0.0–1.0) controlling cursor line width (default: 0.15)
  • blink_interval: Milliseconds for a single on/off blink cycle (default: 750)
  • blink_timeout: Seconds before blinking pauses; 0 disables the pause (default: 5)

ConfigCursorStyle Enum

The ConfigCursorStyle enum (lines 76-84) distinguishes between simple shapes and shapes with explicit blinking overrides:

pub enum ConfigCursorStyle {
    Shape(CursorShape),
    WithBlinking {
        shape: CursorShape,
        blinking: CursorBlinking,
    },
}

CursorShape variants include Block, Underline, Beam, and Hidden. The CursorBlinking enum provides four control levels:

  • Never: Forces blinking off regardless of terminal state
  • Off: Follows the terminal's native blinking flag (off by default)
  • On: Follows the terminal's native blinking flag (on by default)
  • Always: Forces blinking on regardless of terminal state

Runtime Style Resolution

The effective cursor style is computed dynamically in alacritty/src/event.rs within Processor::update_cursor_blinking (lines 1617-1645). This method reconciles configuration defaults with transient states like VI mode, IME composition, and window focus.

The Resolution Algorithm

When the event loop prepares to redraw, Alacritty executes this priority chain:

  1. Base selection: Start with config.cursor.style
  2. VI mode override: If VI mode is active and vi_mode_style is set, substitute the base style
  3. Terminal query: Inspect the terminal's native cursor style via self.terminal.cursor_style().blinking
  4. Blinking resolution:
    • If the config specifies Never or Always, use that override
    • Otherwise, inherit the terminal's native blinking flag
  5. Constraint masking: Enable blinking only if (VI mode OR SHOW_CURSOR mode) is active AND no IME preedit is in progress
  6. Timer scheduling: If blinking is enabled and the window is focused, schedule BlinkCursor and BlinkTimeout events; otherwise, cancel timers and force visibility

The blink_interval value determines the BlinkCursor timer frequency, while blink_timeout controls an optional BlinkTimeout timer that pauses blinking after the specified duration to reduce CPU usage.

Rendering Pipeline

Once the effective style is determined, the display module handles actual pixel generation and visibility toggling.

Shape Selection

In alacritty/src/display/content.rs, the Content::new function (lines 51-62) selects the final shape to render:

  • Returns CursorShape::Hidden if the terminal reports a hidden cursor or during IME pre-edit
  • Renders HollowBlock when the window is unfocused and unfocused_hollow is enabled
  • Otherwise, uses the shape reported by the terminal state machine

Geometry Generation

The display::cursor::IntoRects trait (lines 29-86 in alacritty/src/display/cursor.rs) converts the abstract cursor into GPU-ready RenderRect primitives:

  • Beam: Single vertical rectangle using thickness percentage of cell width
  • Underline: Single horizontal rectangle at the cell bottom
  • Block: Filled rectangle covering the entire cell
  • HollowBlock: Four perimeter rectangles forming an unfilled box

The scheduler in Processor::schedule_blinking (lines 47-53) toggles the display.cursor_hidden boolean on each BlinkCursor event. During the next frame, Content::new checks this flag and treats the cursor as hidden when true, creating the visible blinking effect without regenerating geometry.

Practical Configuration Examples

Customizing alacritty.yml

To configure a blinking beam cursor that pauses after 10 seconds, with a block cursor for VI mode:

cursor:
  style:
    shape: Beam
    blinking: On
  vi_mode_style:
    shape: Block
    blinking: Always
  thickness: 0.20
  blink_interval: 500
  blink_timeout: 10
  unfocused_hollow: true

Accessing Cursor State in Rust

When integrating with Alacritty's internals, access resolved cursor properties through UiConfig:

use alacritty::config::UiConfig;
use std::rc::Rc;

fn inspect_cursor(config: &Rc<UiConfig>) {
    let style = config.cursor.style();
    let vi_override = config.cursor.vi_mode_style();
    let thickness = config.cursor.thickness();
    let blink_ms = config.cursor.blink_interval();
    
    println!("Base style: {:?}, VI override: {:?}", style, vi_override);
}

These methods are defined in alacritty/src/config/cursor.rs (lines 41-63).

After a configuration reload, emit a cursor blinking change event to reset timers:

use alacritty::event::Event;
use alacritty::event::EventType;

let event = Event::new(EventType::CursorBlinkingChange, None);
processor.event_queue.push(event);

This mirrors the implementation in alacritty/src/window_context.rs (lines 28-33).

Key Source Files

The cursor control system spans these critical files in the alacritty/alacritty repository:

Summary

  • Cursor styles and blinking behavior in Alacritty are defined by the Cursor struct in alacritty/src/config/cursor.rs, which supports three shapes and four blinking control levels.
  • Runtime resolution occurs in Processor::update_cursor_blinking (event.rs), which reconciles configuration defaults with VI mode state, terminal flags, and IME status.
  • Blinking animation is driven by scheduled timers (BlinkCursor, BlinkTimeout) that toggle the cursor_hidden display flag between frames.
  • GPU rendering converts the final resolved style into rectangles via IntoRects in display/cursor.rs, respecting the configured thickness percentage.
  • Window focus triggers the hollow block visualization when unfocused_hollow is enabled, handled during shape selection in display/content.rs.

Frequently Asked Questions

How do I disable cursor blinking completely in Alacritty?

Set the blinking field to Never in your alacritty.yml cursor configuration. This forces the cursor to remain visible regardless of terminal application requests or VI mode state. According to the source code in alacritty/src/config/cursor.rs, the Never variant of CursorBlinking acts as an override that suppresses all blinking timers.

Why does my cursor stop blinking after a few seconds?

This is controlled by the blink_timeout configuration option, which defaults to 5 seconds. When the timeout elapses, Alacritty's event processor (in alacritty/src/event.rs) cancels the BlinkCursor timer and leaves the cursor visible to save CPU cycles. Set blink_timeout: 0 to disable this behavior and blink indefinitely.

Does VI mode use a different cursor style automatically?

Only if you explicitly configure vi_mode_style in alacritty.yml. If this field is None (the default), VI mode inherits the base style configuration. When provided, Alacritty's display content logic (in alacritty/src/display/content.rs) checks the VI mode state and switches to the override style immediately upon mode entry.

What controls the cursor thickness in Alacritty?

The thickness field in the cursor configuration accepts a percentage value between 0.0 and 1.0, defaulting to 0.15 (15% of the cell width or height). During rendering in alacritty/src/display/cursor.rs, this percentage is multiplied by the character cell dimensions to calculate the actual pixel width of beam and underline cursors, or the border width of hollow blocks.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →