Tidy up after the sizing rework

The caps model landed in pieces, and the pieces left seams. This joins them
up, and fixes two things the reread turned up.

Deferred subsurface syncing is gone. `needs_tiles` existed because `select`
had no queue handle to sync with, so main.rs grew a bespoke event loop to
notice the flag afterwards. The dispatch handlers are handed a handle
already: pass it down and let `select` do the work itself. main.rs is back
to one uniform `pump`.

The layout is built once, in run(), and passed to App::new, rather than
built there and again inside it. `display` moves from Settings, which is
what App needs, to Options, which is what the caller needs it for.

`timeout = 0` meant an immediate deadline, so uncommenting the line in the
shipped config would have made wl-pick exit before you saw it. Zero now
means no timeout, which is what the comment beside it always claimed.

`timeout` was also settable but documented nowhere -- not in --help, not in
the README. Both now list every key config.rs accepts.

Theme::default's max-width/max-height were placeholder pixel counts that
happened to match one monitor. They are i32::MAX now: no cap of their own,
with Layout clamping to the display.
This commit is contained in:
Milad Alizadeh
2026-09-06 11:01:11 +01:00
parent 86319aa309
commit 1f5b735541
7 changed files with 77 additions and 69 deletions
+10 -19
View File
@@ -57,9 +57,8 @@ fn main() -> ExitCode {
}
fn run() -> Result<ExitCode, Box<dyn Error>> {
let args = cli::parse_args().map_err(|e| -> Box<dyn Error> { e.into() })?;
let config =
Config::load(args.config.as_deref()).map_err(|e| -> Box<dyn Error> { e.into() })?;
let args = cli::parse_args().map_err(Box::<dyn Error>::from)?;
let config = Config::load(args.config.as_deref()).map_err(Box::<dyn Error>::from)?;
let start = Instant::now();
let mut phases = Phases::new(args.verbose);
@@ -88,29 +87,29 @@ fn run() -> Result<ExitCode, Box<dyn Error>> {
phases.mark("sway-tree");
cli::arm_timeout(opts.timeout);
let settings = opts.settings;
let (display, settings) = (opts.display, opts.settings);
let theme = &settings.theme;
let scale = settings.scale;
// The grid is measured once here: the label shaping below and the overlay
// itself must agree about how wide a label may be.
let layout = Layout::new(theme, targets.len() as i32, display);
// Start shaping labels now: it costs ~55ms of font loading and glyph
// rasterising, and the captures below are ~55ms of waiting on the
// compositor, so the two overlap almost exactly.
let labels = theme.labels.then(|| {
let layout = Layout::new(theme, targets.len() as i32, settings.display);
let labels = layout.label(0, 0).map(|label| {
text::spawn(
targets.iter().map(Target::label).collect(),
theme.font.clone(),
theme.font_px * scale as f32,
(theme.line_h * scale) as f32,
// The label box is a tile wide; with no tiles there is nothing to
// shape anyway.
(layout.label(0, 0).map(|r| r.w).unwrap_or(1) * scale) as f32,
(label.w * scale) as f32,
)
});
let conn = Connection::connect_to_env()?;
let (globals, mut queue) = registry_queue_init::<App>(&conn)?;
let qh = queue.handle();
let mut app = App::new(&globals, &qh, targets, settings)?;
let mut app = App::new(&globals, &qh, targets, settings, layout)?;
// Two roundtrips: one for the toplevel list, one for each handle's state.
queue.roundtrip(&mut app)?;
@@ -141,15 +140,7 @@ fn run() -> Result<ExitCode, Box<dyn Error>> {
conn.flush()?;
phases.mark("mapped");
// Scrolling re-places the subsurfaces; doing it here rather than inside the
// key handler coalesces a held-down arrow into one update per dispatch.
while !app.finished() {
queue.blocking_dispatch(&mut app)?;
if std::mem::take(&mut app.needs_tiles) {
app.sync_tiles(&qh);
conn.flush()?;
}
}
pump(&mut queue, &mut app, |a| a.finished())?;
if opts.verbose {
app.report(start.elapsed());
}