Project 12: Copy Mode and Scrollback
Implement scrollback storage and a copy-mode cursor for selecting historical text.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Level 4: Expert |
| Time Estimate | 2 weeks |
| Main Programming Language | C (Alternatives: Rust) |
| Alternative Programming Languages | Rust |
| Coolness Level | Level 4: Text Mechanic |
| Business Potential | 2: The “Power User Feature” |
| Prerequisites | buffer management, text selection |
| Key Topics | scrollback ring buffer, selection mapping, wrap metadata |
1. Learning Objectives
By completing this project, you will:
- Build a working implementation of copy mode and scrollback and verify it with deterministic outputs.
- Explain the underlying Unix and terminal primitives involved in the project.
- Diagnose common failure modes with logs and targeted tests.
- Extend the project with performance and usability improvements.
2. All Theory Needed (Per-Concept Breakdown)
Scrollback Ring Buffer and Selection Mapping
-
Fundamentals Scrollback Ring Buffer and Selection Mapping is the core contract that makes the project behave like a real terminal tool. It sits at the boundary between raw bytes and structured state, so you must treat it as both a protocol and a data model. The goal of the fundamentals is to understand what assumptions the system makes about ordering, buffering, and ownership, and how those assumptions surface as user-visible behavior. Key terms include: ring buffer, wrapped lines, selection, cursor. In practice, the fastest way to gain intuition is to trace a single input through the pipeline and note where it can be delayed, reordered, or transformed. That exercise reveals why Scrollback Ring Buffer and Selection Mapping needs explicit invariants and why even small mistakes can cascade into broken rendering or stuck input.
-
Deep Dive into the concept A deep understanding of Scrollback Ring Buffer and Selection Mapping requires thinking in terms of state transitions and invariants. You are not just implementing functions; you are enforcing a contract between producers and consumers of bytes, and that contract persists across time. Most failures in this area are caused by violating ordering guarantees, dropping state updates, or misunderstanding how the operating system delivers events. This concept is built from the following pillars: ring buffer, wrapped lines, selection, cursor. A reliable implementation follows a deterministic flow: Append output lines to ring -> Track wrap metadata -> Move copy cursor in history -> Map selection to text. From a systems perspective, the tricky part is coordinating concurrency without introducing races. Even in a single-threaded loop, multiple events can arrive in the same tick, so you need deterministic ordering. This is why many implementations keep a strict sequence: read, update state, compute diff, render. Another subtlety is error handling and recovery. A robust design treats errors as part of the normal control flow: EOF is expected, partial reads are expected, and transient failures must be retried or gracefully handled. The deep dive should also cover how to observe the system, because without logs and trace points, you cannot reason about correctness. When you design the project, treat each key term as a source of constraints. For example, if a term implies buffering, decide the buffer size and how overflow is handled. If a term implies state, decide how that state is initialized, updated, and reset. Finally, validate your assumptions with deterministic fixtures so you can reproduce bugs. From a systems perspective, the tricky part is coordinating concurrency without introducing races. Even in a single-threaded loop, multiple events can arrive in the same tick, so you need deterministic ordering. This is why many implementations keep a strict sequence: read, update state, compute diff, render. Another subtlety is error handling and recovery. A robust design treats errors as part of the normal control flow: EOF is expected, partial reads are expected, and transient failures must be retried or gracefully handled. The deep dive should also cover how to observe the system, because without logs and trace points, you cannot reason about correctness. From a systems perspective, the tricky part is coordinating concurrency without introducing races. Even in a single-threaded loop, multiple events can arrive in the same tick, so you need deterministic ordering. This is why many implementations keep a strict sequence: read, update state, compute diff, render. Another subtlety is error handling and recovery. A robust design treats errors as part of the normal control flow: EOF is expected, partial reads are expected, and transient failures must be retried or gracefully handled. The deep dive should also cover how to observe the system, because without logs and trace points, you cannot reason about correctness. From a systems perspective, the tricky part is coordinating concurrency without introducing races. Even in a single-threaded loop, multiple events can arrive in the same tick, so you need deterministic ordering. This is why many implementations keep a strict sequence: read, update state, compute diff, render. Another subtlety is error handling and recovery. A robust design treats errors as part of the normal control flow: EOF is expected, partial reads are expected, and transient failures must be retried or gracefully handled. The deep dive should also cover how to observe the system, because without logs and trace points, you cannot reason about correctness.
-
How this fit on projects This concept is the backbone of the project because it defines how data and control flow move through the system.
-
Definitions & key terms
- ring buffer -> fixed-size circular buffer that overwrites old entries
- wrapped lines -> logical lines that span multiple visual rows
- selection -> a start/end range of text to copy
- cursor -> position used for navigation in copy mode
-
Mental model diagram (ASCII)
[Input] -> [Scrollback Ring Buffer and Selection Mapping] -> [State] -> [Output]
-
How it works (step-by-step, with invariants and failure modes)
- Append output lines to ring
- Track wrap metadata
- Move copy cursor in history
- Map selection to text
-
Minimal concrete example
Cursor at history index 120, column 5 selects across wrapped lines.
-
Common misconceptions
- “History is just a list of strings” -> wrap metadata matters for selection.
-
Check-your-understanding questions
- How do you keep memory bounded?
- How do you map selection across wraps?
-
Check-your-understanding answers
- Use a ring buffer and overwrite oldest lines.
- Store wrap flags and stitch lines for selection.
-
Real-world applications
- tmux copy-mode
-
Where you’ll apply it
- See Section 3.2 Functional Requirements and Section 5.4 Concepts You Must Understand First.
- Also used in: Project 11: Status Bar and Mode Rendering, Project 13: Configuration and Key Bindings.
-
References
- tmux 3 Ch. 7
-
Key insights Scrollback Ring Buffer and Selection Mapping works best when you treat it as a stateful contract with explicit invariants.
-
Summary You now have a concrete mental model for Scrollback Ring Buffer and Selection Mapping and can explain how it affects correctness and usability.
-
Homework/Exercises to practice the concept
- Implement a fixed-size ring for lines.
-
Solutions to the homework/exercises
- Use head/tail indices and modulo arithmetic.
3. Project Specification
3.1 What You Will Build
A scrollback buffer with copy mode that supports moving a cursor through history and selecting text.
3.2 Functional Requirements
- Requirement 1: Store scrollback lines in a ring buffer
- Requirement 2: Enter/exit copy mode
- Requirement 3: Move cursor and select text
- Requirement 4: Copy selection to buffer
3.3 Non-Functional Requirements
- Performance: Avoid blocking I/O; batch writes when possible.
- Reliability: Handle partial reads/writes and cleanly recover from disconnects.
- Usability: Provide clear CLI errors, deterministic output, and helpful logs.
3.4 Example Usage / Output
$ ./mytmux copy-mode
[copy] cursor at line 1200
[copy] selection saved to buffer
[exit code: 0]
$ ./mytmux copy-mode --select 99999
[error] selection out of range
[exit code: 2]
3.5 Data Formats / Schemas / Protocols
Scrollback line struct: text + wrap flags + length.
3.6 Edge Cases
- Selection across wrapped lines
- Scrollback overflow
3.7 Real World Outcome
This section defines a deterministic, repeatable outcome. Use fixed inputs and set TZ=UTC where time appears.
3.7.1 How to Run (Copy/Paste)
make
./mytmux copy-mode
3.7.2 Golden Path Demo (Deterministic)
The “success” demo below is a fixed scenario with a known outcome. It should always match.
3.7.3 If CLI: provide an exact terminal transcript
$ ./mytmux copy-mode
[copy] cursor at line 1200
[copy] selection saved to buffer
[exit code: 0]
Failure Demo (Deterministic)
$ ./mytmux copy-mode --select 99999
[error] selection out of range
[exit code: 2]
3.7.8 If TUI
At least one ASCII layout for the UI:
+------------------------------+
| Copy Mode and Scrollback |
| [content area] |
| [status / hints] |
+------------------------------+
4. Solution Architecture
4.1 High-Level Design
+-----------+ +-----------+ +-----------+
| Client | <-> | Server | <-> | PTYs |
+-----------+ +-----------+ +-----------+
4.2 Key Components
| Component | Responsibility | Key Decisions | |-----------|----------------|---------------| | Scrollback buffer | Stores lines in ring. | Fixed memory budget. | | Copy cursor | Tracks position in history. | Separate from live cursor. | | Selection | Marks start/end and extracts text. | Normalize order before copy. |
4.4 Data Structures (No Full Code)
struct Line { char *text; int len; int wrapped; };
4.4 Algorithm Overview
Key Algorithm: Ring buffer scrollback
- Append lines
- Advance head on overflow
- Translate indices for cursor
Complexity Analysis:
- O(1) append
5. Implementation Guide
5.1 Development Environment Setup
cc --version
make --version
5.2 Project Structure
copy-mode/
|-- src/
| |-- scrollback.c
| `-- copy.c
`-- Makefile
5.3 The Core Question You’re Answering
“How do you let users navigate text that is no longer on screen?”
5.4 Concepts You Must Understand First
- scrollback ring buffer
- Why it matters and how it impacts correctness.
- selection mapping
- Why it matters and how it impacts correctness.
- wrap metadata
- Why it matters and how it impacts correctness.
5.5 Questions to Guide Your Design
- What is the max scrollback size?
- How do you store wrapped lines?
5.6 Thinking Exercise
Design a data structure that maps visual rows to history lines.
5.7 The Interview Questions They’ll Ask
- How does copy mode differ from normal rendering?
5.8 Hints in Layers
- Use a ring buffer with fixed line count.
-
Separate live cursor from copy cursor.
5.9 Books That Will Help
| Topic | Book | Chapter | |——-|——|———| | tmux usage | tmux 3 | Ch. 7 |
5.10 Implementation Phases
Phase 1: Foundation (2 weeks)
Goals:
- Establish the core data structures and loop.
- Prove basic I/O or rendering works.
Tasks:
- Implement the core structs and minimal main loop.
- Add logging for key events and errors.
Checkpoint: You can run the tool and see deterministic output.
Phase 2: Core Functionality (2 weeks)
Goals:
- Implement the main requirements and pass basic tests.
- Integrate with OS primitives.
Tasks:
- Implement remaining functional requirements.
- Add error handling and deterministic test fixtures.
Checkpoint: All functional requirements are met for the golden path.
Phase 3: Polish & Edge Cases (2 weeks)
Goals:
- Handle edge cases and improve UX.
- Optimize rendering or I/O.
Tasks:
- Add edge-case handling and exit codes.
- Improve logs and documentation.
Checkpoint: Failure demos behave exactly as specified.
5.11 Key Implementation Decisions
| Decision | Options | Recommendation | Rationale |
|---|---|---|---|
| I/O model | blocking vs non-blocking | non-blocking | avoids stalls in multiplexed loops |
| Logging | text vs binary | text for v1 | easier to inspect and debug |
6. Testing Strategy
6.1 Test Categories
| Category | Purpose | Examples |
|---|---|---|
| Unit Tests | Validate components | parser, buffer, protocol |
| Integration Tests | Validate interactions | end-to-end CLI flow |
| Edge Case Tests | Handle boundary conditions | resize, invalid input |
6.2 Critical Test Cases
- Scrollback persists across new output
- Selection copies correct text
6.3 Test Data
text
Output 200 lines, scroll back 100, select 3 lines; expect exact text.
7. Common Pitfalls & Debugging
7.1 Frequent Mistakes
| Pitfall | Symptom | Solution | |———|———|———-| | Selection jumps | Wrap metadata missing | Store wrap flags per line. |
7.2 Debugging Strategies
- Dump scrollback indices and cursor positions.
7.3 Performance Traps
-
Copying full history for each selection.
8. Extensions & Challenges
8.1 Beginner Extensions
- Add page-up/page-down navigation.
- Add search within copy mode.
8.2 Intermediate Extensions
- Add word-based selection.
- Add clipboard integration.
8.3 Advanced Extensions
- Add incremental search with highlights.
9. Real-World Connections
9.1 Industry Applications
- terminal emulators
- log viewers
9.2 Related Open Source Projects
- tmux
- less
9.3 Interview Relevance
- Event loops, terminal I/O, and state machines are common interview topics.
10. Resources
10.1 Essential Reading
- tmux 3 by Brian P. Hogan - Ch. 7
10.2 Video Resources
- Copy mode deep dive (talk).
10.3 Tools & Documentation
- less: less
- grep: grep
10.4 Related Projects in This Series
- Project 11: Status Bar and Mode Rendering - Builds prerequisites
-
Project 13: Configuration and Key Bindings - Extends these ideas
11. Self-Assessment Checklist
11.1 Understanding
- I can explain the core concept without notes
- I can explain how input becomes output in this tool
- I can explain the main failure modes
11.2 Implementation
- All functional requirements are met
- All test cases pass
- Code is clean and well-documented
- Edge cases are handled
11.3 Growth
- I can identify one thing I’d do differently next time
- I’ve documented lessons learned
- I can explain this project in a job interview
12. Submission / Completion Criteria
Minimum Viable Completion:
- Tool runs and passes the golden-path demo
- Deterministic output matches expected snapshot
- Failure demo returns the correct exit code
Full Completion:
- All minimum criteria plus:
- Edge cases handled and tested
- Documentation covers usage and troubleshooting
Excellence (Going Above & Beyond):
- Add at least one advanced extension
- Provide a performance profile and improvement notes