-
-
Notifications
You must be signed in to change notification settings - Fork 648
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
Create labelled spaces and move windows on startup #541
Comments
Sorry, I don't have an answer to your question (though I'm curious, how do you first ensure there aren't any spaces?) but just installed Ferdi based on your config. Thanks for that! |
@johnallen3d That's half the problem... I'm not really sure how to ensure there are no spaces (or label the spaces that are there), which is a bit painful because a bunch of spaces get made and then labelled and moved, and then any extra are removed by the script. Ferdi is pretty awesome, it's the free version of Franz. |
The way macOS work you will always have at least one space per monitor. Maybe the solution in #365 could be of some help? |
I was able to (seemingly) get this working by doing the following: python3 ./.config/yabai/yabaictl.py update-spaces
yabai -m space 1 --label main
yabai -m space 2 --label code
yabai -m space 3 --label term
yabai -m space 4 --label chat
yabai -m space 5 --label spotify
yabai -m space main --display 1
yabai -m space chat --display 2
yabai -m space code --display 3
yabai -m space term --display 3
yabai -m space term --move next
yabai -m space spotify --display 3
yabai -m space spotify --move next
yabai -m space spotify --move next It's not ideal but seems to work decently. Moving the spaces using next is clunky though. Probably a better way to go about that. This is needed is due to the python script randomly distributing the spaces, and then moving the spaces to the monitor. I didn't see a better way of ordering spaces after a quick search. |
@Just-Insane could you share the contents of |
@whitepaperclip sure, it's here: #!/usr/local/bin/python3
import json
import subprocess
import click
# these are used to determine display order
setups = {
"home": [
"9D6F6199-E480-45B6-B5B5-F6C1D3C40171",
"F4300722-B985-E97A-DAC9-BC8415D223C6",
"D8BFD1B0-C53A-5A22-141F-5F889FB85E01",
],
"laptop": [
"731FF600-A54A-B317-CF0B-C767EC3D5EB2",
],
}
ignore_messages = [
"acting space is already located on the given display.",
"cannot focus an already focused space.",
]
# TODO: need to handle following errors:
"acting space is the last user-space on the source display and cannot be destroyed."
"acting space is the last user-space on the source display and cannot be moved."
def yabai_message(*msg):
ret = subprocess.run(["yabai", "-m", *msg], capture_output=True)
if ret.returncode:
err_msg = ret.stderr.decode()
if err_msg.strip() not in ignore_messages:
raise Exception(err_msg)
else:
print(f"While running {msg} we received error: {err_msg}")
return ret.stdout.decode()
def yabai_query(domain):
return json.loads(yabai_message("query", "--{}".format(domain)))
class WindowManager:
spaces = []
displays = []
display_order = []
NUM_SPACES = 10
def __init__(self):
self.refresh_state()
@property
def num_displays(self):
return len(self.displays)
@property
def num_spaces(self):
return len(self.spaces)
@property
def unlabled_spaces(self):
return [space for space in self.spaces if space["label"] == ""]
@property
def visible_spaces(self):
return [space for space in self.spaces if space["visible"] > 0]
def refresh_state(self):
self.spaces = yabai_query("spaces")
self.displays = yabai_query("displays")
for setup in setups.values():
if set([display["uuid"] for display in self.displays]) == set(setup):
self.display_order = setup
if self.display_order == []:
print("unidentified setup")
def find_display_index(self, display):
uuid = self.display_order[display]
return next(
display["index"] for display in self.displays if display["uuid"] == uuid
)
def find_space_index(self, space):
return next(
space["index"] for space in self.spaces if space["label"] == f"s{space}"
)
def get_display_for_space(self, space):
return space % self.num_displays - 1
def focus_space(self, space):
yabai_message("space", "--focus", f"s{space}")
def move_space_to_display(self, space, display):
display_index = self.find_display_index(display)
yabai_message(
"space", f"s{space}", "--display", f"{display_index}",
)
def remove_unnecessary_spaces(self):
if self.num_spaces > self.NUM_SPACES:
for unlabled_space in self.unlabled_spaces:
yabai_message("space", f"{unlabled_space['index']}", "--destroy")
def ensure_spaces(self):
if self.num_spaces < self.NUM_SPACES:
for i in range(self.num_spaces, self.NUM_SPACES):
yabai_message("space", "--create")
def ensure_labels(self):
wanted_labels = set(f"s{i}" for i in range(1, self.NUM_SPACES + 1))
existing_labels = set(space["label"] for space in self.spaces)
for ix, missing_label in enumerate(sorted(wanted_labels - existing_labels)):
yabai_message(
"space",
f"{self.unlabled_spaces[ix]['index']}",
"--label",
missing_label,
)
def reorganize_spaces(self):
focused_spaces = self.visible_spaces
for space_index in range(1, self.NUM_SPACES + 1):
self.move_space_to_display(
space_index, self.get_display_for_space(space_index),
)
for space in focused_spaces[: min(self.num_displays, len(focused_spaces))]:
self.focus_space(space["label"].strip("s"))
def update_spaces(self):
self.ensure_spaces()
self.refresh_state()
self.ensure_labels()
self.refresh_state()
self.reorganize_spaces()
self.remove_unnecessary_spaces()
self.refresh_state()
@click.group()
@click.pass_context
def cli(ctx):
# ensure that ctx.obj exists and is a dict (in case `cli()` is called
# by means other than the `if` block below
ctx.ensure_object(dict)
ctx.obj["wm"] = WindowManager()
@cli.command()
@click.pass_context
def update_spaces(ctx):
ctx.obj["wm"].update_spaces()
@cli.command()
@click.argument("space")
@click.pass_context
def focus_space(ctx, space):
ctx.obj["wm"].focus_space(space)
if __name__ == "__main__":
cli(obj={})% It doesn't really work for my use cases due to how it checks for unused spaces and removes them, and how it sends spaces to specific monitors (balanced). |
Awesome, thanks!
|
Is there any way to create labelled spaces and move windows to those spaces on startup?
I have a multiple monitor setup and would like to be able to log in and have my apps all open in the correct spaces on the correct monitors.
Currently, I have this in my config file:
I then have another script that runs on space change to close unfocused spaces without windows, which doesn't really do what I want:
The goal is to create specific spaces on specific monitors and move applications to those spaces.
The text was updated successfully, but these errors were encountered: