Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to move between displays using direction? #225

Closed
chandlerc opened this issue Aug 30, 2019 · 17 comments
Closed

Ability to move between displays using direction? #225

chandlerc opened this issue Aug 30, 2019 · 17 comments
Labels
enhancement New feature or request

Comments

@chandlerc
Copy link

I'm not a macOS expert, so please just let me know if I'm missing obvious things. =]

It seems like the arrangement indices aren't actually shown in the system preferences any more? But you can deduce them from yabai commands. It seems like macOS just assigns them in a fixed sequence based on the display devices. Rearranging the displays doesn't seem to alter the indices in any way.

This makes it hard to have key bindings to move between displays if at different times you use different arrangements.

My ideal feature would be to provide north/east/south/west style navigation based on the arrangement itself. I understand that it would be possible to have ambiguity (two displays "south" of another), but I'd be fine simply picking the first display in the requested direction of the current display. This would still make navigation key bindings much more intuitive to me.

As an alternative if the above isn't possible for some reason (maybe there is no API to discover the arrangement), simply allowing remapping of display arrangement indices would be similarly useful. I could then toggle between several arrangement mappings depending on how I have displays connected.

Thoughts? Is this doable in any way?

(Also, thanks so much for yabai! It's really great!)

@dominiklohmann
Copy link
Collaborator

Changing the order of macOS display arrangement indices comes with many issues because mission-control indices are ongoing across displays.

The query system can help you, though. Best thing you can do is using jq to sort displays queries by coordinates and grab the index of the next one in that sort order (the reverse | nth(... - 1) hack enables wraparound).

# get the index of the next display in a custom sort order 
# the output of display queries has no focused attribute, 
# which is why we need a second query to get the focused display
jq -nr \
    --argjson display "$(yabai -m query --displays --display)" \
    --argjson displays "$(yabai -m query --displays)" \
     '$displays | sort_by(.frame.x + .frame.w / 2, .frame.y + .frame.h / 2) 
                | reverse 
                | nth(index(map(select(.id == $display.id))) - 1)
                .index'

In a similar fashion, you could define a custom arrangement.

# get the index of the 2nd display by center coordinate sort
yabai -m query --displays \
     | jq -r 'sort_by(.frame.x + .frame.w / 2, .frame.y + .frame.h / 2)[1].index'

These indices can the be used in combination with yabai -m display --focus <display selector>.

Display queries carry very little information compared to window queries and space queries, but you can still build many custom commands with them.

@koekeishiya
Copy link
Owner

I'm not particularly well versed in shell scripting, so I'm not sure if the approach mentioned by @dominiklohmann can be used to move between displays using directions as you proposed.

While I'm not aware of any API that will give us the arrangement in a usable way, it would be fairly trivial to construct a spatial mapping using display coordinates that can be used for this purpose.

@koekeishiya koekeishiya added the enhancement New feature or request label Aug 30, 2019
@dominiklohmann
Copy link
Collaborator

dominiklohmann commented Aug 30, 2019

As this issue boards self-proclaimed shell script magician I took it upon me to implement something like this properly.

The below script calculates the angle between all other displays center and the focused displays center coordinate, and gives you the display that matches the target angle best. The target angle is $1 * pi/2, so you can use integers from -2 to 2 for the directions.

# save this to a file somewhere and make it executable
# call with argument from -2 to 2 depending on the direction you want to go in
jq -nr \
	--argjson displays "$(yabai -m query --displays)" \
	--argjson focused "$(yabai -m query --displays --display)" \
	--argjson direction "${1}" \
	'$displays
		| map(select(.id != $focused.id))
		| sort_by((1 | atan * 2 * $direction) 
			- (	  (.frame.y + .frame.h / 2 - $focused.frame.y - $focused.frame.h / 2) 
				/ (.frame.x + .frame.w / 2 - $focused.frame.x - $focused.frame.w / 2) 
				| atan)
			| fabs)
		| first.index // empty' \
	| xargs yabai -m display --focus

@chandlerc
Copy link
Author

That is a magnificent shell script approach.

I do suspect that it might be a tad cleaner to add this directly to the commands, but I'm delighted to have this workaround in the interim. Trying it out now.

@dominiklohmann
Copy link
Collaborator

There's still some weirdness around the 2*pi to 0 border, but I don't think this is worth fixing when @koekeishiya considers this worth implementing natively.

@chandlerc
Copy link
Author

While I have different layouts I care about, they happen to all be linear along the x-axis and so I made a simpler script to give me my linearized "next" and "prev". It also handles saturating at the end and just displays the display index so that I can feed it into any command i want in my key bindings.

The result:

#!/bin/sh

case "${1}" in
  next)
    step=1
    ;;
  prev)
    step=-1
    ;;
  *)
    echo >&2 "ERROR: must provide an argument 'next' or 'prev'!"
    exit 1
    ;;
esac

jq -nr \
  --argjson displays "$(yabai -m query --displays)" \
  --argjson focused "$(yabai -m query --displays --display)" \
  --argjson step "$step" \
  '$displays
    | sort_by(.frame.x)
    | .[index($focused) + $step].index // $focused.index'

But yeah, having this work internally seems quite nice.

Also, thanks for all the help!

@dominiklohmann
Copy link
Collaborator

@chandlerc I believe there's a bug in your script, because jq makes the array index -1 access the last element, so your custom prev does wraparound while your next does not.

@chandlerc
Copy link
Author

chandlerc commented Aug 31, 2019

@chandlerc I believe there's a bug in your script, because jq makes the array index -1 access the last element, so your custom prev does wraparound while your next does not.

Doh, of course. Fixed. In case others are looking for it:

#!/bin/sh

case "${1}" in
  next)
    step=1
    ;;
  prev)
    step=-1
    ;;
  *)
    echo >&2 "ERROR: must provide an argument 'next' or 'prev'!"
    exit 1
    ;;
esac

jq -nr \
  --argjson displays "$(yabai -m query --displays)" \
  --argjson focused "$(yabai -m query --displays --display)" \
  --argjson step "$step" \
  '$displays
    | sort_by(.frame.x)
    | .[index($focused) + if (index($focused) + $step) < 0 then 0 else $step end].index // $focused.index'

Pretty goofy way to clamp, but 🤷🏻‍♂️

@michaelsong-hz
Copy link

Hey, so I'm a former i3 user trying out yabai and I came across this issue. Does this include having the ability to have the focus span across multiple displays like on i3? For example, if I have 2 monitors side by side, I expect that by using the commands below, I'd be able to go from monitor 1 to monitor 2 by hitting "alt + l" if I am on the right most window on monitor 1, and monitor 2 was positioned on the right of monitor 1 in the system. However, right now the focus instead wraps back around to the leftmost window of monitor 1. Is this related to this issue, or should I open a new issue, or did I just miss something in the docs?

# navigate windows and spaces
alt - h : yabai -m window --focus west
alt - j : yabai -m window --focus south
alt - k : yabai -m window --focus north
alt - l : yabai -m window --focus east

@dominiklohmann
Copy link
Collaborator

@michaelsong-hz Window focus does currently not wrap around, and this is unlikely to ever change. Instead, you can rely on the exit code and queries to do wraparound in whatever way fits your setup best.

For example, I have these two keys setup for window focus with wraparound across displays.

alt - tab : yabai -m window --focus next || yabai -m window --focus "$((yabai -m query --spaces --display next || yabai -m query --spaces --display first) | jq -re '.[] | select(.visible == 1)."first-window"')" || yabai -m display --focus next || yabai -m display --focus first
shift + alt - tab : yabai -m window --focus prev || yabai -m window --focus "$((yabai -m query --spaces --display prev || yabai -m query --spaces --display last) | jq -re '.[] | select(.visible == 1)."last-window"')" || yabai -m display --focus prev || yabai -m display --focus last

@webframp
Copy link

I think I should just read the issue threads in this repo to learn from the jq wizardry of @dominiklohmann

@ramblingenzyme
Copy link

Love the scripting solution and I it would be easy to swap in east and west instead of next and prev and it was great! For all of five minutes before I found that when you've got a fullscreen window with other windows beneath, it keeps toggling between the fullscreen window and another window and never goes to another monitor. Not sure how to express that to create a new issue though.

@dominiklohmann
Copy link
Collaborator

@ramblingenzyme See #301

@ramblingenzyme
Copy link

Thanks @dominiklohmann!

@danijar
Copy link

danijar commented May 19, 2020

@ramblingenzyme Do you mind sharing how you adapted the scripts to use west and east instead of prev and next? I didn't manage to have it focus the correct window on the other screen or wrap around from the last screen to the first correctly.

@ramblingenzyme
Copy link

@danijar I didn't, the issue that Dominik links to means it will be fixed in the next release, so I'm just putting up with prev and next for the moment.

@koekeishiya
Copy link
Owner

Implemented on master.

@koekeishiya koekeishiya removed the addressed on master; not released Fixed upstream, but not yet released label Jun 8, 2020
unrevre pushed a commit to unrevre/yabai that referenced this issue Jan 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

7 participants