i3
i3 copied to clipboard
`focus output <direction>` (and `focus <direction>` across outputs) considers only one axis
I'm submitting a…
[ ] Bug [x] Feature Request [ ] Documentation Request [ ] Other (Please describe in detail)
Current Behavior
Consider the following XRandR layout:
When focus is on output 1, focus output right
moves focus to 2.
This seems to be because i3 considers only one axis when choosing the best output to focus.
Desired Behavior
I think moving the focus to 3 would be less surprising.
Impact
[ ] This feature requires new configuration and/or commands
Environment
Output of i3 --moreversion 2>&-
:
Binary i3 version: 4.20.1-78-ga661d82c © 2009 Michael Stapelberg and contributors Running i3 version: 4.20.1-78-ga661d82c (pid 2570446) Loaded i3 config: /home/vladimir/.i3/config (main) (last modified: 2023-05-27T09:33:08 UTC, 0 seconds ago) The i3 binary you just called: /usr/bin/i3 The i3 binary you are running: /usr/bin/i3
- Linux Distribution & Version: Arch Linux - Are you using a compositor (e.g., xcompmgr or compton): picom
Please note that new features which require additional configuration will usually not be considered. We are happy with the feature set of i3 and want to focus in fixing bugs instead. We do accept feature requests, however, and will evaluate whether the added benefit (clearly) outweighs the complexity it adds to i3.
Keep in mind that i3 provides a powerful way to interact with it through its IPC interface: https://i3wm.org/docs/ipc.html.
This patch works for me:
diff --git a/src/randr.c b/src/randr.c
index fb733205..7c5d46da 100644
--- a/src/randr.c
+++ b/src/randr.c
@@ -299,6 +299,26 @@ Output *get_output_next(direction_t direction, Output *current, output_close_far
continue;
}
}
+
+ /* If this output is as close on our direction's axis as the best output,
+ * check the other axis. */
+ if ((direction == D_RIGHT || direction == D_LEFT) && other->x == best->rect.x) {
+ uint32_t cur_center = cur->y + cur->height / 2;
+ int32_t best_distance = abs((best->rect.y + best->rect.height / 2) - cur_center);
+ int32_t other_distance = abs((other->y + other->height / 2) - cur_center);
+ if (other_distance < best_distance) {
+ best = output;
+ continue;
+ }
+ } else if ((direction == D_DOWN || direction == D_UP) && other->y == best->rect.y) {
+ uint32_t cur_center = cur->x + cur->width / 2;
+ int32_t best_distance = abs((best->rect.x + best->rect.width / 2) - cur_center);
+ int32_t other_distance = abs((other->x + other->width / 2) - cur_center);
+ if (other_distance < best_distance) {
+ best = output;
+ continue;
+ }
+ }
}
DLOG("current = %s, best = %s\n", output_primary_name(current), (best ? output_primary_name(best) : "NULL"));
I'll accept this patch with minor adjustments if you submit a PR but it would also need tests:
- include a new test case using
fake_outputs
and a setup similar to yours - correct behavior when wrapping
Let me know if you'd prefer somebody else tackles that.
I wouldn't mind doing so, except for one obstacle: the last time I contributed to i3, I think it took me half a day to get the test suite working. If the situation is unchanged, unfortunately I can't commit to that right now.
The situation hasn't changed. Tbf, it's quite easy to set everything up once you know what is needed but I agree it's not the most elegant setup for a first time. Happy to write the tests for you if you prefer it though.
Yes, please!
Hmm, I am having second thoughts about which output in your example is the most natural to focus. I feel focusing 2
is more natural and consistent, the user can always expect output focus to always move left to right, top to bottom without doing math in their heads.
Important to note that right now it depends on the randr output order, not actually y
coordinates.
@stapelberg @Airblader thoughts?
I feel focusing
2
is more natural and consistent
I can assure you that it is not, especially when you have a window focused at the bottom of 1.
This has been an ongoing annoyance in my setup, until I configured my dotfiles to build/run a fixed i3 version (using Nix).
the user can always expect output focus to always move left to right, top to bottom without doing math in their heads.
I strongly disagree. "left to right, top to bottom" sounds a lot more like "doing math in my head" than "focus the output to the right", which I think most reasonable people would say is 3.
I got bit by this yesterday with a new small extra monitor (drawing tablet) apparently there is no way to define the 'directional' mapping between screens in i3 at all (or even to just excempt a screen from move
/ focus
directional commands (requiring an explicit workspace
'jump' to move focus there).
I understand the extreme reticence to add configurations ... but this seems like a head-scratcher it isn't acknowledged as a must-allow [the configuration of]?
Hmm, I am having second thoughts about which output in your example is the most natural to focus. I feel focusing
2
is more natural and consistent, the user can always expect output focus to always move left to right, top to bottom without doing math in their heads.Important to note that right now it depends on the randr output order, not actually
y
coordinates.@stapelberg @Airblader thoughts?
The way I think about this behavior is through the lens of consistency: The existing focus <direction>
command doesn’t try to do a “match the direction most closely to the current focus” as suggested here.
Consider this layout with multiple splitv containers:
When I focus right
, focus moves to the top right container (highlighted in gray).
IIUC, the focus output <direction>
behavior with multiple monitors is identical, so I don’t think we should change it at this point.
For people who feel strongly about the movement commands not doing what you think they should, I would recommend using i3’s IPC interface to implement your desired behavior. Changing focus is conceptually relatively simple — you fetch the layout tree from i3, find the node that is "focused": true
to understand what the current state of the world is, then send a focus
command with the id of the node you want to focus instead.
The existing
focus <direction>
command doesn’t try to do a “match the direction most closely to the current focus” as suggested here.
I don't understand how that is at all related to the current discussion. focus
for tiled containers has to work with completely different things. Outputs aren't tiled and subdivided rectangles, they are rectangles with arbitrary coordinates and sizes (and are possibly-non-adjacent and possibly-overlapping).
There is no other situation in i3 that it would make sense to compare this to. If i3 did spatial navigation for floating containers, then that would be something to consider, but it does not do that. (Perhaps it should, though!)
Consider this layout with multiple splitv containers:
I don't understand how this is relevant. The outcome there is only so because the top-right window is focused within the right container. If the bottom-right window was focused instead, the outcome would have been different.
In fact, I can use the exact same arguments as yours to show the opposite conclusion.
I'm guessing that you were misled by that the outputs in my illustration have the same width, but of course this is not necessarily true. Unlike your example, outputs 2 and 3 are not in a vertical container (or any "container" that would also exclude 1).
IIUC, the
focus output <direction>
behavior with multiple monitors is identical, so I don’t think we should change it at this point.
No, it is not. It uses a completely different code path, which of course it has to because the representation, geometry, and underlying concepts are completely different.
Instead, I invite you to consider the outcome of using repeated focus left
/ focus right
commands in this situation and variations thereof:
Is the behavior really something that most users would find "natural and consistent"?
I hope you will agree that the current behavior is a violation of the principle of the least surprise. As I hope we've established, the current behavior is not really consistent with anything. Consider the outcome of a hypothetical poll that asks users what they expect to happen after focus right
in the situation illustrated in the original post.
For people who feel strongly about the movement commands not doing what you think they should, I would recommend using i3’s IPC interface to implement your desired behavior. Changing focus is conceptually relatively simple — you fetch the layout tree from i3, find the node that is
"focused": true
to understand what the current state of the world is, then send afocus
command with the id of the node you want to focus instead.
As an aside - yeah, that sounds nice in theory. I already use the IPC for many things. In practice, the IPC is generally quite underpowered and it does not allow many types of tree manipulations that are possible with normal controls (here is one example). It is also very easy to accidentally crash i3 via the IPC; I doubt it has ever been fuzzed. So, the advice to just do it via the IPC comes across a bit sour, sorry.
I don't understand how that is at all related to the current discussion.
focus
for tiled containers has to work with completely different things. Outputs aren't tiled and subdivided rectangles, they are rectangles with arbitrary coordinates and sizes (and are possibly-non-adjacent and possibly-overlapping).
The way I think about it, outputs are just another kind of container.
I would recommend configuring your setup such that outputs are in fact adjacent and aligned. You will likely find other application software (or toolkits, for example) that makes assumptions about outputs.
While outputs can technically be overlapping, that’s not supported in i3.
No, it is not. It uses a completely different code path, which of course it has to because the representation, geometry, and underlying concepts are completely different.
I suggest thinking about this problem independent of code paths and other implementation details. Look at it from the perspective of the user guide instead.
Instead, I invite you to consider the outcome of using repeated
focus left
/focus right
commands in this situation and variations thereof:
Can you spell out the behavior? I’m not in a position to test it right now.
As an aside - yeah, that sounds nice in theory. I already use the IPC for many things. In practice, the IPC is generally quite underpowered and it does not allow many types of tree manipulations that are possible with normal controls (here is one example). It is also very easy to accidentally crash i3 via the IPC; I doubt it has ever been fuzzed. So, the advice to just do it via the IPC comes across a bit sour, sorry.
We tried making everything possible via the IPC interface that seemed achievable.
If you have specific problems with the IPC interface, please file individual issues for those. You’re correct that we never fuzzed the IPC interface (or any other part of i3, really — fuzzing became popular years after i3 was started).
In general, I found your reply needlessly aggressive, and not at all convincing :(. It makes me not want to spend much more time on this issue. Can I ask you to take a deep breath, sleep over it, and then come back focusing on your core argument? Thanks.
In practice, the IPC is generally quite underpowered and it does not allow many types of tree manipulations that are possible with normal controls (here is https://github.com/i3/i3/issues/3564). It is also very easy to accidentally crash i3 via the IPC
Is there any specific bug or crash that blocks you from implementing this in the IPC? Otherwise, I don't see how what you say is relevant. We generally prioritize fixing bugs, especially crashes. There are only a couple of non-trivial-to-fix bugs that affect the IPC but otherwise the statement that "it's very easy to accidentally crash i3 via the IPC" is not generally true.
Consider the outcome of a hypothetical poll that asks users what they expect to happen after focus right in the situation illustrated in the original post.
I've asked people and their response is not what you want it to be ;) My point is not that I know what the outcome of a hypothetical poll would be, but so do you. Even I personally expressed my personal preference for the current behavior:
Hmm, I am having second thoughts about which output in your example is the most natural to focus. I feel focusing
2
is more natural and consistent, the user can always expect output focus to always move left to right, top to bottom without doing math in their heads.
That you later dismissed:
I can assure you that it is not, especially when you have a window focused at the bottom of 1.
All in all, I see that you have a strong fixation in how you think something needs to be done and you become disappointed when people disagree with you. So, I want to join @stapelberg in requesting to re-consider your approach in this.
That is not to say that your usecase is invalid, I do see the merit in what you are proposing but we need to figure out if there's a default behavior that is an overall improvement, if there is any alternative using the IPC or if a new configuration make sense and if the investment in a new option is worth it.
The way I think about it, outputs are just another kind of container.
Do you mean that you would suggest looking at identically-sized adjacent outputs as if they were in a container?
I would recommend configuring your setup such that outputs are in fact adjacent and aligned.
Well, even if 2 and 3 in the original example were actually adjacent, that doesn't change the problem.
Computer monitors can differ in resolution, so I'm not sure if it's reasonable to ask that all i3 users have outputs that are perfectly aligned.
Look at it from the perspective of the user guide instead.
I don't know of anything in the user guide that seems to indicate that the current behavior makes sense. What have I missed?
Can you spell out the behavior? I’m not in a position to test it right now.
Focus will climb up to the topmost two outputs and get "stuck" there.
In general, I found your reply needlessly aggressive, and not at all convincing :(. It makes me not want to spend much more time on this issue. Can I ask you to take a deep breath, sleep over it, and then come back focusing on your core argument? Thanks.
I apologize for the tone. In truth I am frustrated because, from my point of view, the project maintainers of a project dear to me were refusing to acknowledge that a certain nonsensical behavior is nonsensical, and their arguments about why the current behavior makes sense did not make sense to me at all.
Is there any specific bug or crash that blocks you from implementing this in the IPC? Otherwise, I don't see how what you say is relevant.
I admit it wasn't relevant, sorry.
I will do this via the IPC, so I am going to close this issue, but if you feel like it, we can continue the discussion.
but otherwise the statement that "it's very easy to accidentally crash i3 via the IPC" is not generally true.
OK, it must just be my personal experience, which I admit may be atypical. I have crashed i3 many times while developing my own IPC helper.
I've asked people and their response is not what you want it to be ;)
That is unfair. As a program's developer, I am burdened with and biased by the knowledge of the program's inner workings, and can't trust myself to make good decisions that affect the user experience.
That you later dismissed:
I have taken some liberty here because I have first-hand experience with trying to use such a setup. I feel that perhaps first-hand experience may be qualitatively different than theoretical hypotheses, but I suppose we can agree to disagree.
All in all, I see that you have a strong fixation in how you think something needs to be done and you become disappointed when people disagree with you. So, I want to join @stapelberg in requesting to re-consider your approach in this.
I feel that this was unnecessarily judgemental on your part.
I am generally happy to disagree and collaborate in finding the best solution, but I feel like this isn't what happened here. I still don't see why the current behavior makes more sense (especially considering that it currently actually depends on internal output order instead of their geometry), and all the presented arguments attempting to justify it so far seem erroneous to me. But, I'm fine with dropping it in the interests of everyone's time.
Having slept on this, I think I know of a way how to make this work closer to what I think Michael has envisioned it.
The reason why focus right
in Michael's example does what it does is because Michael had focused the top-right window within the right container before focusing the left container. If the bottom-right window had been focused instead, focus had been moved to it.
If we want to treat outputs as close as possible as containers, I think we can do the same thing here: when breaking a tie between outputs with the same coordinate along the axis we're moving focus in, we should pick the output that had been most recently focused. Then, it will work like in Michael's example, and I believe it will work well in all of the cases discussed above, and should more consistent with how i3 behaves in general.
To sum it up:
- Currently, the tie-breaker is the output index, which is a bit arbitrary.
- I had originally proposed making the tie-breaker the distance of the output from the current output along the other axis.
- Orestis suggested using the raw coordinate as the tie-breaker (top-most or left-most wins).
- I think a better solution would like to use the last focus time as the tie-breaker (most recently focused wins).
Using the last-focused output as a tie-breaker sounds like a good suggestion to me. @orestisfl, what do you think?
Most recently focused is consistent with how container focus works, great suggestion :+1:
@CyberShadow I've worked a lot with focus order previously so I can submit a PR with tests a bit later in the next couple days.
what's the best summary of the focus rules you might link me to?
Unlike containers which are dynamic, physical displays are not. In my case I believe using the heuristic above will be worse than it currently will, because it will require keeping a mental stack of what screens I've used to successfully jump.