From 5b98615c410ebb2d46e813620c50367c64fad865 Mon Sep 17 00:00:00 2001 From: Milad Alizadeh Date: Sun, 23 Aug 2026 18:35:27 +0100 Subject: [PATCH] Rename to wl-pick MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wlgrid described the shape of the thing rather than what it is for, and the shape is the least interesting part now that it doesn't focus anything: it shows you what is running and reports which one you pointed at. wl-pick says that. The wl- prefix rather than wlr- is deliberate. The capture path is entirely upstream ext- protocols (ext-image-copy-capture, ext-image-capture-source, ext-foreign-toplevel-list); the only wlroots-flavoured piece is layer-shell, which KDE, niri and labwc implement too. What actually keeps this from running on other compositors is the sway IPC dependency, not wlroots, so wlr- would promise a portability that isn't there. Hyphenated because the ecosystem hyphenates when the suffix is a real word — wl-clipboard, wl-mirror, wl-screenrec — and reserves the smushed form for coinages like wlsunset. Also renames the layer-shell namespace and the memfd labels, which show up in compositor debugging. --- Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 16 ++++++++-------- src/main.rs | 32 ++++++++++++++++---------------- src/sway.rs | 2 +- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4a5d90b..f5d81b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -605,7 +605,7 @@ dependencies = [ ] [[package]] -name = "wlgrid" +name = "wl-pick" version = "0.1.0" dependencies = [ "cosmic-text", diff --git a/Cargo.toml b/Cargo.toml index 573b509..696be4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "wlgrid" +name = "wl-pick" version = "0.1.0" edition = "2024" description = "Live window switcher for wlroots compositors: a thumbnail grid with the look of a rofi theme" diff --git a/README.md b/README.md index c67dbbd..9c95a32 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# wlgrid +# wl-pick A window switcher for wlroots compositors: a grid overlay of **live** window previews that looks like a rofi theme, and tells you which one you picked. @@ -35,7 +35,7 @@ is the one thing the rofi version had that this doesn't — see the roadmap. ## Usage ``` -wlgrid [--print] [--verbose] [--hide-labels] [--font FAMILY] [--font-size PX] +wl-pick [--print] [--verbose] [--hide-labels] [--font FAMILY] [--font-size PX] [--live all|current|none] [--fps N] [--timeout SECS] ``` @@ -48,17 +48,17 @@ wlgrid [--print] [--verbose] [--hide-labels] [--font FAMILY] [--font-size PX] - `--timeout SECS` exits after a deadline (an escape hatch: the overlay takes an exclusive keyboard grab) -wlgrid is a chooser: it reports what you picked and leaves acting on it to the +wl-pick is a chooser: it reports what you picked and leaves acting on it to the caller. The pick goes to stdout, nothing does if you cancel, and the exit status is 0 for a pick and 1 for a cancel. -wlgrid never acts on the choice — it has no idea what you want to do with it. +wl-pick never acts on the choice — it has no idea what you want to do with it. Focusing on sway looks like this: ```sh #!/usr/bin/env bash # ~/.local/bin/winmenu, bound to $mod+Tab -IFS=$'\t' read -r type id toplevel app title < <(wlgrid) || exit 0 +IFS=$'\t' read -r type id toplevel app title < <(wl-pick) || exit 0 case $type in window) swaymsg "[con_id=$id] focus" ;; output) swaymsg "focus output $id" ;; @@ -68,7 +68,7 @@ esac Windows only, as a one-liner: ```sh -swaymsg "[con_id=$(wlgrid --no-outputs | cut -f2)] focus" +swaymsg "[con_id=$(wl-pick --no-outputs | cut -f2)] focus" ``` Three formats, because the identifiers different consumers need differ: @@ -80,13 +80,13 @@ Three formats, because the identifiers different consumers need differ: | `portal` | `Monitor: NAME` or `Window: TOPLEVEL_ID` | `portal` is exactly what xdg-desktop-portal-wlr's `simple` chooser reads, so -wlgrid can be the picker for `getDisplayMedia` and friends — with live previews +wl-pick can be the picker for `getDisplayMedia` and friends — with live previews of both windows and displays: ```ini [screencast] chooser_type=simple -chooser_cmd=wlgrid --format portal +chooser_cmd=wl-pick --format portal ``` | key | | diff --git a/src/main.rs b/src/main.rs index d26240d..facfdb5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -//! wlgrid shows a live grid of every window and display as a layer-shell overlay +//! wl-pick shows a live grid of every window and display as a layer-shell overlay //! and reports which one you picked. That is all it does: acting on the choice //! belongs to whatever called it. It replaces a wlthumbs + rofi pipeline, so the //! look comes straight from that rofi theme (see theme.rs). @@ -385,7 +385,7 @@ impl App { self.pool_bytes = total; // Note: no mmap. The compositor writes these pages and samples them // again for display; mapping them here would only cost us the faults. - let file = shm::memfd("wlgrid-capture", total)?; + let file = shm::memfd("wl-pick-capture", total)?; let pool = self.shm.create_pool(file.as_fd(), total as i32, qh, ()); for (i, slot_offsets) in offsets.iter().enumerate() { let (w, h, format) = { @@ -515,7 +515,7 @@ impl App { &surface, None, // let the compositor place it on the active output Layer::Overlay, - "wlgrid".to_string(), + "wl-pick".to_string(), qh, (), ); @@ -526,7 +526,7 @@ impl App { let (pw, ph) = (lw * self.scale, lh * self.scale); let len = shm::Chrome::slot_len(pw, ph) * shm::Chrome::SLOTS; - let file = shm::memfd("wlgrid-chrome", len)?; + let file = shm::memfd("wl-pick-chrome", len)?; let pool = self.shm.create_pool(file.as_fd(), len as i32, qh, ()); for slot in 0..shm::Chrome::SLOTS { self.chrome_buffers.push(pool.create_buffer( @@ -724,9 +724,9 @@ fn pump( } const HELP: &str = "\ -wlgrid — a live grid of window and display previews, for picking one +wl-pick — a live grid of window and display previews, for picking one -usage: wlgrid [options] +usage: wl-pick [options] --format tsv|json|portal how to report the pick [tsv] --live all|current|none which tiles keep updating live [all] @@ -766,11 +766,11 @@ formats: [screencast] chooser_type=simple - chooser_cmd=wlgrid --format portal + chooser_cmd=wl-pick --format portal focusing on sway: - IFS=$'\\t' read -r type id toplevel app title < <(wlgrid) && + IFS=$'\\t' read -r type id toplevel app title < <(wl-pick) && case $type in window) swaymsg \"[con_id=$id] focus\" ;; output) swaymsg \"focus output $id\" ;; @@ -852,7 +852,7 @@ fn main() -> ExitCode { match run() { Ok(code) => code, Err(e) => { - eprintln!("wlgrid: {e}"); + eprintln!("wl-pick: {e}"); ExitCode::FAILURE } } @@ -865,7 +865,7 @@ fn run() -> Result> { if let Some(d) = args.timeout { std::thread::spawn(move || { std::thread::sleep(d); - eprintln!("wlgrid: timeout"); + eprintln!("wl-pick: timeout"); std::process::exit(2); }); } @@ -955,7 +955,7 @@ fn run() -> Result> { ); } eprintln!( - "wlgrid: {} window(s), {matched} matched, {ready} captured; \ + "wl-pick: {} window(s), {matched} matched, {ready} captured; \ grid {}x{}, surface {}x{} logical at scale {}, {} MB of capture buffers", app.tiles.len(), app.layout.cols, @@ -980,7 +980,7 @@ fn run() -> Result> { let frames: u32 = app.tiles.iter().map(|t| t.frames).sum(); let live_for = start.elapsed().as_secs_f64(); eprintln!( - "wlgrid: {frames} frame(s) over {live_for:.1}s = {:.1}/s, {} tick(s), \ + "wl-pick: {frames} frame(s) over {live_for:.1}s = {:.1}/s, {} tick(s), \ {} release(s), {} blocked; per tile: {}", frames as f64 / live_for, app.ticks, @@ -994,10 +994,10 @@ fn run() -> Result> { ); } - // wlgrid is a chooser: it reports the pick, and what that means is the + // wl-pick is a chooser: it reports the pick, and what that means is the // caller's business. if args.verbose { - eprintln!("wlgrid: {}", app.quit_why); + eprintln!("wl-pick: {}", app.quit_why); } let Some(target) = app.activate else { return Ok(ExitCode::FAILURE); // cancelled: nothing on stdout @@ -1010,7 +1010,7 @@ fn run() -> Result> { None => { // The portal can only name a window by its foreign-toplevel // identifier, and this one has none; silence means declined. - eprintln!("wlgrid: {:?} has no toplevel identifier", target.title); + eprintln!("wl-pick: {:?} has no toplevel identifier", target.title); return Ok(ExitCode::FAILURE); } }, @@ -1126,7 +1126,7 @@ impl Dispatch for App { // frame yet leaves the tile without a thumbnail. if tile.frames == 0 { eprintln!( - "wlgrid: capture failed for {:?} ({reason:?})", + "wl-pick: capture failed for {:?} ({reason:?})", tile.target.title ); tile.failed = true; diff --git a/src/sway.rs b/src/sway.rs index ab7f3e0..bab8d62 100644 --- a/src/sway.rs +++ b/src/sway.rs @@ -2,7 +2,7 @@ //! Wayland side only supplies pixels. The join between the two is //! `foreign_toplevel_identifier`, which sway reports per view. //! -//! Acting on the choice is deliberately not here: wlgrid reports what was picked +//! Acting on the choice is deliberately not here: wl-pick reports what was picked //! and the caller decides what that means. use swayipc::{Connection, Node, NodeType};