Skip to content

Add all-outputs mode for #78#79

Closed
jason-curtis wants to merge 8 commits into
moverest:mainfrom
jason-curtis:main
Closed

Add all-outputs mode for #78#79
jason-curtis wants to merge 8 commits into
moverest:mainfrom
jason-curtis:main

Conversation

@jason-curtis
Copy link
Copy Markdown

This implements a multi-monitor simultaneous mode. wl-kbptr --all-outputs -o modes=tile,click now shows a tiled grid across all monitors, allowing user to select a tile across multiple displays.

Currently works in my testing in tile mode. If you like it I can look at adding floating mode as well. It's not obvious to me how this would work in bisect/split modes so my inclination is to leave those out.

jason-curtis and others added 5 commits February 16, 2026 21:44
When --all-outputs (or general.all_outputs=true in config) is set,
wl-kbptr creates a layer-shell surface on every connected output
simultaneously. The first surface receives exclusive keyboard focus;
the rest are visual-only. All surfaces share a single label namespace
spanning the global bounding box of all outputs, so the user can type
any visible label regardless of which display currently has window focus.

The tile mode works without changes: it renders in global coordinates
and each per-output cairo context is translated by (-output.x, -output.y)
so cells naturally clip to each output's bounds.

After a selection, resolve_result_output() identifies which output
contains the result centre, sets state->current_output, and converts
the result rect to output-local coordinates for move_pointer.

Backwards compatible: defaults to single-output mode. The existing
-O, -r, and single-output behaviour are unchanged.

Closes moverest#78
Cell density: compute sub_area_size from average logical monitor area
rather than the full bounding box so each monitor gets the same cell
density as in single-output mode (same ~676 cells worth of size per
monitor, distributed across the bounding box).

Dead zones: when the result centre lands in a gap between monitors
(empty bounding box space with no output), snap it to the nearest
output boundary rather than falling back to the first output at
arbitrary coordinates.
Documents the --all-outputs / -A flag, its tile-mode-only scope,
cell density behaviour, and dead zone handling.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
In all-outputs mode, cells whose centre falls in a gap between monitors
(empty bounding-box space not covered by any output) no longer get labels
assigned. This keeps the full label budget for reachable cells, reducing
keystroke count on multi-monitor setups with unaligned displays.

A cell_idx_map[] array maps label-index -> linear cell-index for the
valid subset. The map is NULL in single-output mode (identity mapping).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@moverest
Copy link
Copy Markdown
Owner

moverest commented Feb 17, 2026

I haven't read the code in details yet, however I have a few thoughts.

There are two ways to think about it, either:

  • We have specified a region and in this case the grid should span that. In this doesn't change the behaviour for the bisect, split and float modes.
  • We haven't specified a region and in this case:
    • For the floating mode, we work in absolute positioning instead of it being relative to the output.
    • For the tiling mode, we cover all the outputs. Though, there's still the question output positioning and potential overlapping. I'm not convinced by the dead-zones approach though (see below).
    • For the bisect and split the user should probably just chose the output first, then it continues as normal.

Now for the dead-zones... I'm not sure this is the right approach. Having tested your changes for one it doesn't cover the full screens which means areas that can't be selected. On top of that, this means having to keep track of these.

Looking at the problem a better way to think about it would be to think in terms of regions. Each screen is a region and each region has a number of identifiers associated with it (we want a similar density for each). Then we render all the regions with continuous indexes. No need to keep track of dead zones with this method.

Where things get more interesting is if regions overlap:

  • If two regions completely overlap or one is included into the other, e.g. we mirroring, then it simply a matter of removing one of them.
  • If they don't then we need to subtract the overlap for one region. Below are the cases I can think of:
overlap-1 overlap-2
overlap-3 overlap-4

This way we cover the whole displays and it should make things easier. We "just" need to determine the regions then the number of selectable area per region and then render with these parameters.

FYI, I've tried your code and it segfault when I chose an area. Not sure it's worth investigating if it's going to change.

jason-curtis and others added 2 commits February 17, 2026 13:28
Instead of computing a global bounding-box grid and skipping cells
whose centre falls in a gap between monitors, treat each monitor as an
independent region with its own rows×cols grid.  Labels are indexed
continuously across all regions with no dead zones.

- Add struct tile_region (area, rows/cols, cell dimensions, label offset)
- tile_mode_enter: build a regions[] array in all-outputs mode; keep the
  single-output flat-grid path unchanged
- tile_mode_key: look up the selected label's region and compute its
  cell rect directly
- tile_mode_render: extract render_cell() helper; iterate per-region in
  all-outputs mode; keep the flat-grid render path for single output
- tile_mode_state_free: free regions[]

Benefits over the previous approach:
- Every label maps to a real, clickable cell on a real monitor
- Full label budget is used (no wasted labels for inter-monitor gaps)
- Naturally handles overlapping/mirrored displays (each output is its
  own region regardless of spatial overlap)
- No segfault-prone global-coordinate dead-zone snap logic needed

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Remove cell_idx_map: it was always NULL after the region-based
  rewrite; drop from tile_mode_state, the constructor, render loop,
  key handler, and free
- calloc instead of malloc in tile_mode_enter so sub_area_* fields are
  zero-initialised in the multi-output path
- Fix overlay_surface pointer alignment: wp_fractional_scale_v1 was
  one column off from the other pointer fields
- handle_layer_surface_configure: remove redundant double-set of
  overlay->configured; set it once before the branch and use a local
  was_configured for the else-if guard
- render_cell comment: fix reference to ms->label_selection (not a
  parameter); rename to match the actual argument
- resolve_result_output comment: describe the snap fallback accurately
  rather than calling it a dead-zone case
- Revert two accidental char* spacing changes in main.c that diverged
  from upstream style without reason
- README: update "How it works" bullet to say per-region labels, not
  global bounding box; remove stale Dead zones section; add config
  file example for all_outputs; tighten Cell density paragraph
- config: add parse_bool and register all_outputs so it can be set in
  the config file as well as via -A

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@jason-curtis
Copy link
Copy Markdown
Author

Thanks for the feedback. Implemented a new approach with a grid per output. Now the grid for each display, lines up with the monitor edges.

Supporting arbitrary overlap cases is substantially more complicated but I'll give it a shot. It may be reasonable to skip support for overlapping outputs, or maybe just for simple and more common cases (mirroring or strict subset only?).

Thoughts?

@jason-curtis
Copy link
Copy Markdown
Author

jason-curtis commented Feb 18, 2026

I think we should probably close this in favor of #78, which addresses your concerns and also adds floating mode support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants