A small Linux daemon that silently takes a screenshot every N seconds and appends it to a compressed H.264/Matroska video. Designed to keep running in the background, survive crashes without corrupting the archive, and stay out of the way — visible only as a tray icon.
- Periodic multi-monitor capture via
xcap(X11 primary, Wayland best-effort). Monitors are composited side-by-side into one canvas per frame. - Continuous H.264 encoding into MKV segments via linked
libav(noffmpegsubprocess). MKV is chosen because truncated files remain playable. - Automatic segment rotation when the monitor layout changes (resolution or
connect/disconnect of a display), when
segment_minuteselapses, or at local midnight (so each MKV covers a single calendar day by default). - Crash resilience: staged JPEGs are only deleted once the corresponding
frame is flushed and fsynced; MKV muxer runs with
flush_packets=1. Any orphan JPEGs from a prior run are drained intorecovery_*.mkvat startup. - System tray icon with Pause / Resume / Quit. Icon shows current status (blue open eye = recording, gray closed eye = paused).
- Single TOML config at
~/.config/blink/config.toml(written on first run).
The project targets Linux (X11 + GTK). It currently assumes the versions of
libav that ship with Ubuntu 24.04 (libav*.so.60).
The .devcontainer here installs the full toolchain:
Reopen in Container → cargo build --release
Install native dependencies (apt package names for Ubuntu 24.04):
pkg-config
libavformat-dev libavcodec-dev libavutil-dev libswscale-dev
libavfilter-dev libavdevice-dev
libgtk-3-dev libayatana-appindicator3-dev
libxcb1-dev libxrandr-dev libxfixes-dev libxext-dev libxdo-dev
libssl-dev
Then install a stable Rust toolchain via rustup and:
cargo build --release
The binary is target/release/blink (~4 MB stripped).
blink # start the daemon (same as `blink run`)
blink config-path # print path to config.toml
blink help
Start blink once and leave it sitting in the tray; pause/resume from there.
To launch it automatically on login, drop the bundled desktop entry into the XDG autostart directory:
install -Dm644 config/blink.desktop ~/.config/autostart/blink.desktop
The session manager (GNOME, KDE, XFCE, …) will then start blink as part of your graphical session — independent of any terminal — and it will reappear in the tray after every login.
First run writes a default config to ~/.config/blink/config.toml.
Default paths:
- Frames staging:
~/.cache/blink/staging/(each frame as a timestamped JPEG until it has been successfully encoded; normally empty) - Video output:
~/Videos/blink/(blink_YYYYMMDD-HHMMSS_WxH.mkv) - PID lock:
~/.cache/blink/blink.pid
The tray menu offers:
- Pause / Resume — toggles capture. Icon changes to reflect status.
- Quit — clean shutdown: flush encoder, write MKV trailer, remove PID file, exit.
The daemon also handles SIGINT and SIGTERM as graceful shutdown, so
system shutdown or a kill <pid> from a terminal leaves properly finalized
files.
~/.config/blink/config.toml:
[capture]
interval_seconds = 60 # one frame per minute
monitors = "all" # "all" | "primary"
[video]
codec = "h264" # h264 | h265 | av1
crf = 28 # lower = higher quality, larger files
segment_minutes = 60 # roll over to a new MKV every hour
daily_split = true # also roll over at local midnight (one MKV per day)
[staging]
jpeg_quality = 85
keep_after_encode = false # keep the JPEGs on disk after encoding
# (enable to feed a future OCR pass)
[output]
dir = "" # empty → ~/Videos/blink
[daemon]
pid_file = "" # empty → ~/.cache/blink/blink.pid
log_file = "" # (reserved; current build logs to stderr)| Scenario | Resulting file | Frame loss |
|---|---|---|
Quit from tray, SIGINT, SIGTERM, logout |
Properly finalized MKV with trailer | None |
SIGKILL, kernel panic, power cut |
Valid MKV without trailer (plays fine up to last flushed cluster) | ≤ 1 in-flight frame |
| Daemon exits then restarts with JPEGs left in staging | Drained into recovery_*.mkv on next startup |
None |
.
├── .devcontainer/ VS Code devcontainer (Rust + libav + GTK + X11)
├── config/default.toml Embedded default config, written on first run
├── config/blink.desktop XDG autostart entry (copy to ~/.config/autostart/)
├── Cargo.toml
└── src/
├── main.rs CLI, thread orchestration, signal handling
├── config.rs TOML loader, XDG path resolution
├── state.rs Shared AtomicBool flags + PID file guard
├── staging.rs Atomic JPEG writes + pending_frames scan
├── capture.rs xcap screenshot loop, monitor composite
├── encoder.rs libav MKV muxer, segment rotation, recovery
└── tray.rs AppIndicator tray icon + GTK menu
- Text journal: OCR each frame and write timestamped text entries. The code
already names staged JPEGs
<unix_millis>.jpgand retains them whenkeep_after_encode = true, so a futureblink ocrsubcommand can iterate them in timestamp order without touching the capture/encode pipeline.
MIT