Rename to wl-pick
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.
This commit is contained in:
Generated
+1
-1
@@ -605,7 +605,7 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wlgrid"
|
name = "wl-pick"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cosmic-text",
|
"cosmic-text",
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wlgrid"
|
name = "wl-pick"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
description = "Live window switcher for wlroots compositors: a thumbnail grid with the look of a rofi theme"
|
description = "Live window switcher for wlroots compositors: a thumbnail grid with the look of a rofi theme"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# wlgrid
|
# wl-pick
|
||||||
|
|
||||||
A window switcher for wlroots compositors: a grid overlay of **live** window
|
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.
|
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
|
## 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]
|
[--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
|
- `--timeout SECS` exits after a deadline (an escape hatch: the overlay takes an
|
||||||
exclusive keyboard grab)
|
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
|
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.
|
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:
|
Focusing on sway looks like this:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# ~/.local/bin/winmenu, bound to $mod+Tab
|
# ~/.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
|
case $type in
|
||||||
window) swaymsg "[con_id=$id] focus" ;;
|
window) swaymsg "[con_id=$id] focus" ;;
|
||||||
output) swaymsg "focus output $id" ;;
|
output) swaymsg "focus output $id" ;;
|
||||||
@@ -68,7 +68,7 @@ esac
|
|||||||
Windows only, as a one-liner:
|
Windows only, as a one-liner:
|
||||||
|
|
||||||
```sh
|
```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:
|
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` | `Monitor: NAME` or `Window: TOPLEVEL_ID` |
|
||||||
|
|
||||||
`portal` is exactly what xdg-desktop-portal-wlr's `simple` chooser reads, so
|
`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:
|
of both windows and displays:
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
[screencast]
|
[screencast]
|
||||||
chooser_type=simple
|
chooser_type=simple
|
||||||
chooser_cmd=wlgrid --format portal
|
chooser_cmd=wl-pick --format portal
|
||||||
```
|
```
|
||||||
|
|
||||||
| key | |
|
| key | |
|
||||||
|
|||||||
+16
-16
@@ -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
|
//! 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
|
//! belongs to whatever called it. It replaces a wlthumbs + rofi pipeline, so the
|
||||||
//! look comes straight from that rofi theme (see theme.rs).
|
//! look comes straight from that rofi theme (see theme.rs).
|
||||||
@@ -385,7 +385,7 @@ impl App {
|
|||||||
self.pool_bytes = total;
|
self.pool_bytes = total;
|
||||||
// Note: no mmap. The compositor writes these pages and samples them
|
// Note: no mmap. The compositor writes these pages and samples them
|
||||||
// again for display; mapping them here would only cost us the faults.
|
// 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, ());
|
let pool = self.shm.create_pool(file.as_fd(), total as i32, qh, ());
|
||||||
for (i, slot_offsets) in offsets.iter().enumerate() {
|
for (i, slot_offsets) in offsets.iter().enumerate() {
|
||||||
let (w, h, format) = {
|
let (w, h, format) = {
|
||||||
@@ -515,7 +515,7 @@ impl App {
|
|||||||
&surface,
|
&surface,
|
||||||
None, // let the compositor place it on the active output
|
None, // let the compositor place it on the active output
|
||||||
Layer::Overlay,
|
Layer::Overlay,
|
||||||
"wlgrid".to_string(),
|
"wl-pick".to_string(),
|
||||||
qh,
|
qh,
|
||||||
(),
|
(),
|
||||||
);
|
);
|
||||||
@@ -526,7 +526,7 @@ impl App {
|
|||||||
|
|
||||||
let (pw, ph) = (lw * self.scale, lh * self.scale);
|
let (pw, ph) = (lw * self.scale, lh * self.scale);
|
||||||
let len = shm::Chrome::slot_len(pw, ph) * shm::Chrome::SLOTS;
|
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, ());
|
let pool = self.shm.create_pool(file.as_fd(), len as i32, qh, ());
|
||||||
for slot in 0..shm::Chrome::SLOTS {
|
for slot in 0..shm::Chrome::SLOTS {
|
||||||
self.chrome_buffers.push(pool.create_buffer(
|
self.chrome_buffers.push(pool.create_buffer(
|
||||||
@@ -724,9 +724,9 @@ fn pump(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const HELP: &str = "\
|
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]
|
--format tsv|json|portal how to report the pick [tsv]
|
||||||
--live all|current|none which tiles keep updating live [all]
|
--live all|current|none which tiles keep updating live [all]
|
||||||
@@ -766,11 +766,11 @@ formats:
|
|||||||
|
|
||||||
[screencast]
|
[screencast]
|
||||||
chooser_type=simple
|
chooser_type=simple
|
||||||
chooser_cmd=wlgrid --format portal
|
chooser_cmd=wl-pick --format portal
|
||||||
|
|
||||||
focusing on sway:
|
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
|
case $type in
|
||||||
window) swaymsg \"[con_id=$id] focus\" ;;
|
window) swaymsg \"[con_id=$id] focus\" ;;
|
||||||
output) swaymsg \"focus output $id\" ;;
|
output) swaymsg \"focus output $id\" ;;
|
||||||
@@ -852,7 +852,7 @@ fn main() -> ExitCode {
|
|||||||
match run() {
|
match run() {
|
||||||
Ok(code) => code,
|
Ok(code) => code,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("wlgrid: {e}");
|
eprintln!("wl-pick: {e}");
|
||||||
ExitCode::FAILURE
|
ExitCode::FAILURE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -865,7 +865,7 @@ fn run() -> Result<ExitCode, Box<dyn Error>> {
|
|||||||
if let Some(d) = args.timeout {
|
if let Some(d) = args.timeout {
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
std::thread::sleep(d);
|
std::thread::sleep(d);
|
||||||
eprintln!("wlgrid: timeout");
|
eprintln!("wl-pick: timeout");
|
||||||
std::process::exit(2);
|
std::process::exit(2);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -955,7 +955,7 @@ fn run() -> Result<ExitCode, Box<dyn Error>> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
eprintln!(
|
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",
|
grid {}x{}, surface {}x{} logical at scale {}, {} MB of capture buffers",
|
||||||
app.tiles.len(),
|
app.tiles.len(),
|
||||||
app.layout.cols,
|
app.layout.cols,
|
||||||
@@ -980,7 +980,7 @@ fn run() -> Result<ExitCode, Box<dyn Error>> {
|
|||||||
let frames: u32 = app.tiles.iter().map(|t| t.frames).sum();
|
let frames: u32 = app.tiles.iter().map(|t| t.frames).sum();
|
||||||
let live_for = start.elapsed().as_secs_f64();
|
let live_for = start.elapsed().as_secs_f64();
|
||||||
eprintln!(
|
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: {}",
|
{} release(s), {} blocked; per tile: {}",
|
||||||
frames as f64 / live_for,
|
frames as f64 / live_for,
|
||||||
app.ticks,
|
app.ticks,
|
||||||
@@ -994,10 +994,10 @@ fn run() -> Result<ExitCode, Box<dyn Error>> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
// caller's business.
|
||||||
if args.verbose {
|
if args.verbose {
|
||||||
eprintln!("wlgrid: {}", app.quit_why);
|
eprintln!("wl-pick: {}", app.quit_why);
|
||||||
}
|
}
|
||||||
let Some(target) = app.activate else {
|
let Some(target) = app.activate else {
|
||||||
return Ok(ExitCode::FAILURE); // cancelled: nothing on stdout
|
return Ok(ExitCode::FAILURE); // cancelled: nothing on stdout
|
||||||
@@ -1010,7 +1010,7 @@ fn run() -> Result<ExitCode, Box<dyn Error>> {
|
|||||||
None => {
|
None => {
|
||||||
// The portal can only name a window by its foreign-toplevel
|
// The portal can only name a window by its foreign-toplevel
|
||||||
// identifier, and this one has none; silence means declined.
|
// 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);
|
return Ok(ExitCode::FAILURE);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1126,7 +1126,7 @@ impl Dispatch<ExtImageCopyCaptureFrameV1, usize> for App {
|
|||||||
// frame yet leaves the tile without a thumbnail.
|
// frame yet leaves the tile without a thumbnail.
|
||||||
if tile.frames == 0 {
|
if tile.frames == 0 {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"wlgrid: capture failed for {:?} ({reason:?})",
|
"wl-pick: capture failed for {:?} ({reason:?})",
|
||||||
tile.target.title
|
tile.target.title
|
||||||
);
|
);
|
||||||
tile.failed = true;
|
tile.failed = true;
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
//! Wayland side only supplies pixels. The join between the two is
|
//! Wayland side only supplies pixels. The join between the two is
|
||||||
//! `foreign_toplevel_identifier`, which sway reports per view.
|
//! `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.
|
//! and the caller decides what that means.
|
||||||
|
|
||||||
use swayipc::{Connection, Node, NodeType};
|
use swayipc::{Connection, Node, NodeType};
|
||||||
|
|||||||
Reference in New Issue
Block a user