Explorar o código

fix(yabai): better implementation of second monitor support

Joe hai 1 ano
pai
achega
71cd7ea88e
Modificáronse 3 ficheiros con 119 adicións e 44 borrados
  1. 1 0
      .config/skhd/skhdrc
  2. 118 39
      .config/yabai/yabai.py
  3. 0 5
      .scripts/open_application.zsh

+ 1 - 0
.config/skhd/skhdrc

@@ -6,6 +6,7 @@ cmd - n : \
 	fi
 
 cmd + shift - n : /bin/zsh $XDG_CONFIG_HOME/yabai/yabairc
+cmd + shift - m : /usr/bin/env python3 $XDG_CONFIG_HOME/yabai/yabai.py invert
 cmd + shift - d: /bin/zsh $HOME/.scripts/lightmode.zsh toggle
 
 cmd - 1 : /bin/zsh $HOME/.scripts/open_application.zsh terminal

+ 118 - 39
.config/yabai/yabai.py

@@ -58,6 +58,7 @@ class Space:
             f"Space({self.label if self.label and len(self.label) > 0 else '<NoLabel>'}"
             f", {self.index}"
             f"{', Fullscreen' if self.is_native_fullscreen else ''}"
+            f", {self.display}"
             f"{', Focused' if self.has_focus else ''})"
         )
 
@@ -105,15 +106,18 @@ class CLIWrapper:
 class Yabai(CLIWrapper):
     _base_args: list[str] = ["yabai", "-m"]
 
+    # Label, Full-Page, Apps, Main Display
     spaces: list[tuple[str, bool, list[str], bool]] = [
         ("Desktop", False, ["*"], True),
-        ("Finder", False, ["Finder"], False),
-        ("Terminal", True, ["Alacritty"], False),
-        ("Browser", True, ["Firefox"], False),
+        ("Finder", False, ["Finder"], True),
+        ("Terminal", True, ["Alacritty"], True),
+        ("Browser", True, ["Firefox"], True),
     ]
     _initial_window: Window | None = None
     _dual_display: None | Literal[True] | Literal[False] = None
     _exit_with_rule_apply: bool = False
+    _exit_with_refocus: bool = False
+    _display_labels: tuple[str, str] = ("Main", "Secondary")
 
     def __init__(self):
         self._dual_display = len(self.get_displays()) > 1
@@ -123,7 +127,7 @@ class Yabai(CLIWrapper):
                     "Communication",
                     False,
                     ["Slack", "Signal", "Spotify"],
-                    True,
+                    False,
                 )
             )
             for application in [
@@ -139,7 +143,7 @@ class Yabai(CLIWrapper):
                         application,
                         True,
                         [application],
-                        True,
+                        False,
                     )
                 )
         else:
@@ -162,6 +166,7 @@ class Yabai(CLIWrapper):
             debug(f"Exited with {exc_type} {exc_value}")
         if self._exit_with_rule_apply:
             self.message(["rule", "--apply"])
+        if self._exit_with_refocus or self._exit_with_rule_apply:
             if self._initial_window is not None:
                 self.message(["window", "--focus", self._initial_window.id])
         if exc_type is None:
@@ -195,11 +200,17 @@ class Yabai(CLIWrapper):
     def manage_displays(self):
         displays = self.get_displays()
         main_display = self.get_main_display(displays)
+        secondary_display = None
         for display in displays:
             if display.index == main_display.index:
-                self.message(["display", display.index, "--label", "Main"])
-            elif display.index == main_display.index + 1:
-                self.message(["display", display.index, "--label", f"Secondary"])
+                self.message(
+                    ["display", display.index, "--label", self._display_labels[0]]
+                )
+            elif secondary_display == None:
+                self.message(
+                    ["display", display.index, "--label", self._display_labels[1]]
+                )
+                secondary_display = display
             else:
                 self.message(
                     [
@@ -423,16 +434,7 @@ class Yabai(CLIWrapper):
 
         # Rules that differ for one or multiple displays
         if self._dual_display:
-            # space
-            self.message(
-                [
-                    "signal",
-                    "--add",
-                    "event=space_changed",
-                    "label=SpaceChanged",
-                    f"action=/usr/bin/env python3 {HOME}/.config/yabai.py move",
-                ]
-            )
+            pass
         else:
             # Google Meet and Slack Huddles should be "sticky"
             for app, title in (("Google Meet", ".*"), ("Slack", "Huddle.*")):
@@ -461,21 +463,23 @@ class Yabai(CLIWrapper):
                 ]
             )
 
-    def move_spaces(self):
-        initial_window = self.get_focused_window()
-        final_window = self.get_focused_window()
-
+    def move_spaces_to_displays(self):
+        if not self._dual_display:
+            return
+        current_spaces = self.get_spaces()
         displays = self.get_displays()
-        main_display = self.get_main_display(displays)
+        main_display = next(
+            (d for d in displays if d.label == self._display_labels[0]), None
+        )
         secondary_display = next(
-            (d for d in displays if not d.id == main_display.id), None
+            (d for d in displays if d.label == self._display_labels[1]), None
         )
-
-        current_spaces = self.get_spaces()
+        if main_display is None or secondary_display is None:
+            return
 
         incorrect_main_spaces: set[Space] = set()
         incorrect_secondary_spaces: set[Space] = set()
-        for space_label, _, _, is_secondary_space in self.spaces:
+        for space_label, _, _, is_main_display_space in self.spaces:
             mapped_space = next(
                 (s for s in current_spaces if s.label == space_label), None
             )
@@ -484,21 +488,27 @@ class Yabai(CLIWrapper):
 
             if (
                 mapped_space.display == main_display.index
-                and is_secondary_space
+                and not is_main_display_space
                 and secondary_display
             ):
                 incorrect_secondary_spaces.add(mapped_space)
-            if mapped_space.display != main_display.index and not is_secondary_space:
+            elif mapped_space.display != main_display.index and is_main_display_space:
                 incorrect_main_spaces.add(mapped_space)
+        last_focus = next((s.label for s in current_spaces if s.has_focus), None)
         while len(incorrect_main_spaces) > 0 and len(incorrect_secondary_spaces) > 0:
+            from_space = incorrect_main_spaces.pop()
+            to_space = incorrect_secondary_spaces.pop()
+            if to_space.label == last_focus:
+                from_space, to_space = to_space, from_space
             self.message(
                 [
                     "space",
-                    incorrect_main_spaces.pop().label,
+                    from_space.label,
                     "--switch",
-                    incorrect_secondary_spaces.pop().label,
+                    to_space.label,
                 ]
             )
+            last_focus = to_space.label
         for spaces, secondary in (
             (incorrect_main_spaces, False),
             (incorrect_secondary_spaces, True),
@@ -515,12 +525,24 @@ class Yabai(CLIWrapper):
                     ]
                 )
 
-        if (
-            final_window is not None
-            and initial_window is not None
-            and final_window.id != initial_window.id
-        ):
-            self.message(["window", "--focus", initial_window.id])
+    def sort_spaces(self):
+        all_spaces = [s.label for s in sorted(self.get_spaces())]
+        for is_main in (True, False) if self._dual_display else [None]:
+            order = [
+                s[0]
+                for s in self.spaces
+                if s[0] in all_spaces and (s[3] == is_main) or is_main is None
+            ]
+            spaces = [s for s in all_spaces if s in order]
+
+            while tuple(spaces) != tuple(order) and len(spaces) == len(order):
+                for index in range(1, len(spaces)):
+                    if order.index(spaces[index]) < order.index(spaces[index - 1]):
+                        self.message(["space", spaces[index], "--move", "prev"])
+                        spaces[index], spaces[index - 1] = (
+                            spaces[index - 1],
+                            spaces[index],
+                        )
 
     def get_focused_window(self) -> Window | None:
         windows = [
@@ -534,8 +556,54 @@ class Yabai(CLIWrapper):
             return windows.pop()
         return None
 
+    def invert_displays(self) -> None:
+        if not self._dual_display:
+            return
+        displays = self.get_displays()
+        for display in displays:
+            self.message(
+                [
+                    "display",
+                    display.index,
+                    "--label",
+                    f"TEMP{display.label}",
+                ]
+            )
+        for display in displays:
+            if display.label == self._display_labels[0]:
+                self.message(
+                    [
+                        "display",
+                        display.index,
+                        "--label",
+                        self._display_labels[1],
+                    ]
+                )
+            elif display.label == self._display_labels[1]:
+                self.message(
+                    [
+                        "display",
+                        display.index,
+                        "--label",
+                        self._display_labels[0],
+                    ]
+                )
+            else:
+                self.message(
+                    [
+                        "display",
+                        display.index,
+                        "--label",
+                        display.label,
+                    ]
+                )
+
     def enable_exit_with_rule_apply(self):
         self._exit_with_rule_apply = True
+        self.enable_exit_with_refocus()
+
+    def enable_exit_with_refocus(self):
+        self._exit_with_refocus = True
 
 
 if __name__ == "__main__":
@@ -548,5 +616,16 @@ if __name__ == "__main__":
             yabai.manage_spaces()
         if argv[1] == "initialize":
             yabai.set_rules_and_signals()
-        if argv[1] == "move" or argv[1] == "manage" or argv[1] == "initialize":
-            yabai.move_spaces()
+        if argv[1] == "invert":
+            yabai.invert_displays()
+            yabai.enable_exit_with_rule_apply()
+        if (
+            argv[1] == "move"
+            or argv[1] == "manage"
+            or argv[1] == "initialize"
+            or argv[1] == "invert"
+        ):
+            yabai.move_spaces_to_displays()
+            # Disabled until moving spaces no longer triggers mission control
+            # yabai.sort_spaces()
+            yabai.enable_exit_with_refocus()

+ 0 - 5
.scripts/open_application.zsh

@@ -44,11 +44,6 @@ if [ "$valid_selection" = true ] ; then
     active_display=$(jq 'map(select(."has-focus"==true)) | map(.display) | first' <<< $spaces)
     active_space=$(jq 'map(select(."has-focus"==true)) | map(.index) | first' <<< $spaces)
     other_space=$(jq 'map(select(."has-focus"==false and ."is-visible"==true)) | map(.index) | first' <<< $spaces)
-    # If the selected window is on the secondary monitor, swap it to the main one
-    if [ "$active_display" != "1" ]; then
-        yabai -m space $active_space --switch $other_space
-        yabai -m space $active_space --focus
-    fi
     # If there are 3+ windows in the space, then make selected the largest window
     shared_windows=$(jq --argjson space "$active_space" 'map(select(.space==$space)) | map( { ("size"): (.frame.w * .frame.h), "id": .id, "focus": ."has-focus", "title": .title } ) | sort_by(.size)' <<< $windows)
     focused_window=$(jq 'map(select(.focus)) | first | .id' <<< $shared_windows)