Skip to content

BendyLand/zclip

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

45 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

zclip

zclip is a lightweight clipboard daemon for Linux written in Zig. It sits in the background, watches the X11 clipboard, and automatically builds a unique list of recent clipboard entries. A small CLI talks to the daemon over a UNIX domain socket so you can list, recall, save/load to SQLite, or clear items without an entire GUI manager.

Features

  • Daemonized background service (zclip with no args)
  • Automatic capture of clipboard changes using XFixes (with periodic fallback polling)
  • De-duplication and stable insertion order
  • Fast recall: set the X11 clipboard to any saved entry via zclip get N
  • Manual push: pipe data to zclip push (reads stdin)
  • Persistence: save/load the set to /tmp/zclip.db (SQLite)
  • Simple IPC over a UNIX socket at /tmp/zclip.sock
  • Crash-safe ergonomics: clear messages, easy recovery (remove stale socket, check logs)

How it works (high level)

  • On start, the daemon creates an invisible X11 window and registers for XFixes selection owner notifications on the CLIPBOARD atom.
  • When a new owner appears, it requests UTF8_STRING and stores the text if it’s new.
  • It also performs periodic polling as a fallback.
    • This was also necessary to be able to push specific entries back to the system clipboard (xclip).
  • Items live in a MasterList (string → insertion index).
    • A Tray view is derived by sorting keys by index for display.
  • The CLI connects to /tmp/zclip.sock and sends plain-text commands (e.g., list, get 3).
  • get forks a short-lived helper that temporarily becomes the selection owner, serves the requestor, then exits.

Requirements

  • Zig (recent stable)
  • X11 headers & libraries: libX11, libXfixes
  • dlopen/dlsym (libdl) and POSIX bits (poll, signals)
  • SQLite plus a Zig SQLite binding

To easily install the requirement, run the following command (Debian/Ubuntu):

sudo apt install zig libx11-dev libxfixes-dev libdl-dev sqlite3 libsqlite3-dev

Zig dependency note

  • This program uses the vrischmann/zig-sqlite package, added via build.zig.zon and imported as @import("sqlite").
// build.zig.zon
.dependencies = .{
    .sqlite = .{
        .url = "git+https://github.com/vrischmann/zig-sqlite#be8b4965b46fc1a7a819bf3cba09f370c0e9c64c",
        .hash = "sqlite-3.48.0-F2R_a9GLDgAXT-c49TfkFMt6yPOMQAYfp4ig8bRNdZs4",
    },
},

// build.zig
const sqlite_dep = b.dependency("sqlite", .{
    .target = target,
    .optimize = optimize,
});
exe.root_module.addImport("sqlite", sqlite_dep.module("sqlite"));
  • Notes:
    • You still need the system SQLite development files installed so the package can link against libsqlite3
    • (Debian/Ubuntu): sudo apt install libsqlite3-dev

Build & Run

# The 'safe' release option is what is used during testing.
# Behavior or performance may vary with other options.
zig build --release=safe 

# Start the daemon (no args). It will daemonize itself.
# Logs go to: /tmp/zclip.log
# Socket is at: /tmp/zclip.sock
./zig-out/bin/zclip
# It is recommended to move the binary to /usr/local/bin 
# or add the binary's directory to your system $PATH.
# That way you can simply run `zclip`. 
# You may also alias it to something like `zc` for ease of use. 
# (For the remainder of this README, I will use `zclip` as the working command.)

Quick start

# Show help (no daemon needed; will still work if daemon is running)
zclip help

# Start daemon (no args):
zclip

# Copy something in your X11 session (Ctrl+C somewhere or run):
echo "This is a test" | xclip -sel clip

# List saved items:
zclip list

# Set clipboard to item 3 (and print it to stdout as a response):
zclip get 3

# Manually push via stdin (reads all stdin):
echo -n "hello world" | zclip push
# NOTE: This does *not* update your system clipboard. 
# To actually be able to paste that text, you must run `zclip get 9999` (which gets the last item; assuming you don't actually have over 9999 items)

# Save current set to sqlite:
zclip save

# Load previously saved set from sqlite:
zclip load

# Prints the number of saved items:
zclip len

# Clear all saved items (does not kill daemon):
zclip clear

# Ask daemon to exit cleanly:
zclip exit

Command reference (client → daemon)

zclip...

  • push <text> — Add an entry (client usually provides via stdin).
  • get <n> — Set system clipboard to entry n (1-based; out-of-range clamps to ends).
  • last — Alias for get 10000 (will effectively return the most recently saved item).
  • list — Print all items with 1-based indices (run zclip help to see flags).
  • len — Print the number of tray items.
  • on — Prints whether the daemon is already running.
  • clear — Remove all saved items from memory.
  • save — Persist current set to /tmp/zclip.db.
  • load — Load from /tmp/zclip.db (replaces in-memory set).
  • help — Prints the help menu (same as zclip help).
  • exit — Shut down the daemon (removes /tmp/zclip.sock).
  • reset — Convenience macro:
    • Pushes "", performs get 10000 (effectively last item), then clears everything.
    • Result: both the current clipboard entry and in-memory list end up empty.

Paths & files

  • UNIX socket: /tmp/zclip.sock
  • Daemon log: /tmp/zclip.log
    • (also a short-lived /tmp/zclip-set.log for the forked setter helper)
  • SQLite DB: /tmp/zclip.db

Troubleshooting

“Daemon not running”

  • Start it with zclip (no args). Check /tmp/zclip.log.

“Connection refused” or commands hang

  • The daemon likely crashed or exited uncleanly. Remove stale socket:
  • rm -f /tmp/zclip.sock then start the daemon again.

No items saved

  • Ensure you’re actually on X11. Wayland native sessions won’t work.
  • Make sure libXfixes is available; without it you’ll rely on polling only.
  • Some apps set non-UTF8 clipboard formats; zclip requests UTF8_STRING.

Permission/SELinux/AppArmor issues

  • Check that your user can create and connect to /tmp/zclip.sock, and that the environment has access to the X11 display.

Security & Privacy Notes

  • Clipboard contents are stored in memory and can be persisted to disk in plain text (/tmp/zclip.db).
  • If you copy secrets, be aware they may linger there.
  • Consider relocating the socket/DB to a private runtime dir for multi-user systems.

License

This project is licensed under the MIT License.

About

A clipboard manager daemon for Linux. Store multiple strings, retrieve old ones, and even save them to a SQLite db for later use (can be read back into the daemon or used directly)!

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages