workspace: match i3's depth-first workspace-output assignment

When selecting which workspace to create on an output, i3 uses a
"depth-first" approach: for each workspace, it checks if the current
output is the *first available* choice in that workspace's output list.
This allows configurations like:

    workspace 1 output DP-1 eDP-1
    workspace 9 output eDP-1

When only eDP-1 is connected, i3 would select workspace 1 (since eDP-1
is its first available output). Previously, sway would check if the
output was *anywhere* in the workspace's list, which could lead to
unexpected behavior depending on config order.

This change updates both workspace_valid_on_output() and
workspace_next_name() to use the same "first available" logic as i3,
making sway's workspace-output assignment behavior match i3 more
closely.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Milad Alizadeh 2026-01-06 21:40:40 +00:00
parent 8224d5fcf8
commit d43038b141
No known key found for this signature in database

View file

@ -201,9 +201,14 @@ static bool workspace_valid_on_output(const char *output_name,
return true;
}
// Match i3's behavior: a workspace is only valid on an output if that
// output is the first available choice in the workspace's output list.
for (int i = 0; i < wsc->outputs->length; i++) {
if (output_match_name_or_id(output, wsc->outputs->items[i])) {
return true;
struct sway_output *first_available =
output_by_name_or_id(wsc->outputs->items[i]);
if (first_available) {
// Found the first available output - valid only if it matches
return first_available == output;
}
}
@ -319,11 +324,21 @@ char *workspace_next_name(const char *output_name) {
continue;
}
bool found = false;
// Find the first available output for this workspace, matching i3's
// depth-first behavior: only select this workspace if the current
// output is the first available choice in its output list.
for (int j = 0; j < wsc->outputs->length; ++j) {
if (output_match_name_or_id(output, wsc->outputs->items[j])) {
found = true;
free(target);
target = strdup(wsc->workspace);
struct sway_output *first_available =
output_by_name_or_id(wsc->outputs->items[j]);
if (first_available) {
// Found the first available output for this workspace
if (first_available == output) {
// Current output is the first choice - select this workspace
found = true;
free(target);
target = strdup(wsc->workspace);
}
// Always break: we only care about the first available output
break;
}
}