DEC Private Modes

DEC Private Modes are terminal control sequences that enable or disable specific terminal behaviors like cursor visibility, mouse tracking, alternate screen buffers, and modern features like synchronized output.

The blessed library provides a clean API for working with these modes:

  • Query mode support with get_dec_mode()

  • Enable modes with dec_modes_enabled() context manager

  • Disable modes with dec_modes_disabled() context manager

Each mode is identified by a constant from Terminal.DecPrivateMode. Our mode catalog is derived from https://wiki.tau.garden/dec-modes/

Overview

DEC Private Modes control a wide variety of terminal features. Some common examples include:

  • DECTCEM (25) - Cursor visibility

  • MOUSE_REPORT_CLICK (1000) - Basic mouse click reporting

  • MOUSE_EXTENDED_SGR (1006) - Extended mouse reporting with pixel coordinates

  • BRACKETED_PASTE (2004) - Receive clipboard paste as a single event

  • FOCUS_IN_OUT_EVENTS (1004) - Detect when terminal window gains/loses focus

  • SYNCHRONIZED_OUTPUT (2026) - Eliminate screen flicker during redraws

The context managers gracefully handle unsupported modes - your code works normally even on terminals that don’t support specific features.

Timeout Behavior

All DEC mode context managers use a default timeout of 1 second when querying mode support. This timeout is designed to handle “dumb” terminals that don’t support DEC mode queries and won’t respond.

The timeout delay only occurs on the first call to any mode query method, as subsequent calls use cached results. Unsupported modes are gracefully ignored - your code continues to work even when a terminal doesn’t support a specific feature.

You can verify mode support quickly using get_dec_mode() (see Querying Mode Support below), which also benefits from caching after the first query.

Getting Started

Here’s a simple example that temporarily hides the cursor:

 1#!/usr/bin/env python3
 2from blessed import Terminal
 3
 4term = Terminal()
 5
 6print("Watch the cursor disappear, ")
 7with term.dec_modes_disabled(term.DecPrivateMode.DECTCEM):
 8    print("Cursor is hidden - working...")
 9    term.inkey(2)
10
11print()
12print("Cursor is back!")

The cursor automatically reappears when the context exits, even if an exception occurs.

This usually emits the same sequences recorded in the terminfo database of modern terminals for the term.hide_cursor and term.normal_cursor attributes and offered by our context manager method, hidden_cursor().

The difference is that we can also make inquiries into the whether the mode is supported at all.

Querying Mode Support

You can check if a terminal supports a specific mode using get_dec_mode(). This is useful for adapting your application to different terminal capabilities:

 1#!/usr/bin/env python3
 2from blessed import Terminal
 3
 4term = Terminal()
 5
 6# Query mouse support
 7mode = term.DecPrivateMode(term.DecPrivateMode.MOUSE_REPORT_CLICK)
 8response = term.get_dec_mode(mode)
 9
10print(f"Checking {mode.name} (mode {mode.value}) {mode.long_description}: ", end="")
11
12if response.supported:
13    status = "enabled" if response.enabled else "disabled"
14    state = "permanently" if response.permanent else "temporarily"
15    print(f"Supported and {status} {state}")
16elif response.failed:
17    print("Terminal does not support DEC mode queries")
18else:
19    print("Mode not supported by this terminal")

The timeout value is unspecified and defaults to 1 second. If a Terminal fails to respond in this amount of time, the special property failed becomes True.

The DecModeResponse object provides helper properties:

Query results are automatically cached. Use force=True to bypass the cache:

Try the display-modes.py example program to detect and report all supported sequences for a given terminal.

Quick Boolean Detection

For the most commonly used modes, convenience methods return a simple boolean without needing to inspect the full DecModeResponse:

  • does_bracketed_paste() – Bracketed Paste (mode 2004)

  • does_synchronized_output() – Synchronized Output (mode 2026)

  • does_grapheme_clustering() – Grapheme Clustering (mode 2027)

  • does_focus_events() – Focus In/Out Events (mode 1004)

term = Terminal()

if term.does_synchronized_output():
    print("Synchronized output supported")

if term.does_bracketed_paste():
    print("Bracketed paste supported")

These methods accept the same timeout parameter as get_dec_mode() and benefit from the same caching.

Context Managers

The recommended way to work with modes is through context managers:

  • dec_modes_enabled() - Temporarily enable one or more modes

  • dec_modes_disabled() - Temporarily disable one or more modes

These context managers:

  1. Query the Terminal’s support for a mode within given timeout

  2. Change the mode if allowed by negotiation

  3. Restore the original state on exit if changed

  4. Handle unsupported modes gracefully

You can pass multiple modes to enable them simultaneously:

with term.dec_modes_enabled(
    term.DecPrivateMode.DECTCEM,
    term.DecPrivateMode.MOUSE_REPORT_CLICK,
):
    # Both modes enabled here
    pass

For commonly-used DEC modes, blessed provides convenient context managers on the Terminal object that make accessing them easier.

These convenience wrappers all contain a default timeout argument of 1. Given this, for terminals that do not support DEC Mode negotiation, the first call will cause up to a 1 second delay while awaiting a possible terminal response to confirm support.

All subsequent calls use a cache of the failed query result and incur no further delays.

Synchronized Output

Synchronized Output (mode 2026) eliminates screen flicker by buffering all output until the mode is exited. This is perfect for animations and full-screen redraws.

Without synchronized output, rapidly clearing and redrawing the screen creates a visible blink effect. With it, updates appear instantly:

 1#!/usr/bin/env python3
 2from blessed import Terminal
 3
 4term = Terminal()
 5
 6fill = "█" * term.height * term.width
 7empty = " " * term.height * term.width
 8
 9print(term.bold_red("Warning! Screen may blink rapidly!"))
10print()
11print("Press return to continue, 'q' to stop test")
12term.inkey()
13
14with term.fullscreen():
15    for step in range(300):
16        with term.synchronized_output():
17            print(term.home + empty, flush=True)
18            print(term.home + fill, flush=True)
19            print(term.home + f'step={step}')
20        if term.inkey(0.01) == 'q':
21            break
22
23print(term.clear + "Test complete!")

On terminals that support this mode, you’ll just see screen of fill characters with a counter in the top-left corner. On terminals that do not support it, partial draws of empty spaces will cause the screen to occasionally blink or flash.

Bracketed Paste

Bracketed Paste (mode 2004) allows your application to receive clipboard paste operations as a single event rather than a stream of individual characters. This makes it easy to distinguish between typed and pasted text:

 1#!/usr/bin/env python3
 2from blessed import Terminal
 3
 4term = Terminal()
 5
 6print("Paste some text (press 'q' to quit)...")
 7
 8with term.bracketed_paste():
 9    with term.cbreak():
10        while True:
11            ks = term.inkey()
12
13            if ks.name == 'BRACKETED_PASTE':
14                print(f"Pasted: {term.reverse(repr(ks.text))}")
15            elif ks == 'q':
16                print("Goodbye!")
17                break
18            elif ks:
19                print(f"Regular key: {ks!r}")

When Keystroke.mode equals BRACKETED_PASTE, the mode_values() method returns a BracketedPasteEvent with a text attribute containing the pasted content.

Focus Events

Focus tracking (mode 1004) reports when the terminal window gains or loses focus. This is useful for pausing animations or updating status indicators:

 1#!/usr/bin/env python3
 2from blessed import Terminal
 3
 4term = Terminal()
 5
 6print("Switch focus to/from this terminal window, 'q' to stop.")
 7
 8with term.focus_events():
 9    with term.cbreak():
10        while True:
11            inp = term.inkey()
12            if inp.name == 'FOCUS_IN':
13                print("Focus gained")
14            elif inp.name == 'FOCUS_OUT':
15                print("Focus lost")
16            elif inp == 'q':
17                break

When Keystroke.mode equals FOCUS_IN_OUT_EVENTS, the mode_values() method returns a FocusEvent with a gained attribute indicating whether focus was gained (True) or lost (False).