Eigentlich brauchte ich nie einen Window-Manager, von denen es ja für den Mac zuhauf gibt. Die angeschlossene Bildschirmanzahl bleibt bei meinen Rechnern gleich und ich muss auch keine zwei Programmfenster genau fifty/fifty auf dem Bildschirm verteilen.

Dann bin ich allerdings über den WindowmanagerSlate gestolpert, der einige interessante Ansätze bot, für dich sogar ich Verwendung fand.
Zu allem hin ist Slate OpenSource.

Taucht man etwas tiefer in die Materie ein, landet man aber irgendwie sogleich bei Hammerspoon (ebenfalls OpenSource), was (nicht nur) ein Fenstermanager ist, sondern eine sehr mächtige Automatisierungslösung für MacOS X.

Diese beiden Programme möchte ich im Folgenden unter dem Gesichtspunkt "ziemlich aufgebohrte Fenstermanager" mal vorstellen.

Eines vorweg...

Beide Programme muss man unter Systemeinstellungen > Sicherheit > Bedienungshilfen frei geben, um mit ihnen arbeiten zu können.

Man wird allerdings beim ersten Start darauf hingewiesen.

 

Sowohl bei Slate wie auch bei Hammerspon verwende ich eigene Tastenkürzel, um Aktionen auszulösen.
Will man programmübergreifend Tastenkürzel verwenden, ist man eher auf der sicheren Seite, nicht mit dem Tastenküzel des Programmes im Vordergrund zu kollidieren, umso mehr Modifier-Tasten (Shift + ctrl + alt + cmd) man zusammen drückt.
5 Tasten gleichzeitig zu drücken kann aber auch etwas mühsam sein. Zum Glück bin ich irgendwo auf den Tipp gestoßen, die ansonsten unliebsame und unbenutzte (habe sie unter Systemeinstellungen > Tastatur > Tastatur > Sondertasten de-aktiviert) CapsLock-Taste zu verwenden.
Mit dem Programm Karabiner Elements (wechselt gerne den Namen: ab macOS 10.12 Karabiner Elements, macOS 10.9-10.11 Karabiner, macOS 10.4-10.8 KeyRemap4MacBook) lässt sich die CapsLock-Taste so modifizieren, dass jedes Programm denkt, man würde Shift + ctrl + alt + cmd drücken, wenn man nur CapsLock gedrückt hält.

Slate

OpenSource

Info und Download:
https://github.com/jigish/slate/

Dokumentation/API:
https://github.com/jigish/slate/wiki
 
Slate ist ziemlich flexibel konfigurierbar, aber für den typischen GUI-verwöhnten Anwender etwas ungewohnt, da die ganze Konfiguration in einer Textdatei gemacht wird.
Es gibt einerseits die Konfigurations-Datei .slate, neuerdings (in einem mehr oder weniger experimentellen Stadium) noch eine weitere JavaScript-fähige .slate.js; beide im Home-Verzeichnis des Nutzers.
Diese beiden Dateien muss man aber ersteinmal selbst erstellen – sie werden nicht selbstständig angelegt, falls nicht vorhanden.
Es kann entweder nur eine von beiden vorhanden sein oder beide. Sind beide vorhanden, wird die JavaScript-Konfigurations-Datei nach der anderen geladen.

Von Slate sieht man nicht viel – nur ein Icon in der Menüleiste, über welches man eben jene Konfigurations-Dateien neu laden und das Programm beenden kann.

Was kann man mit Slate machen?

Zum Beispiel per Hotkey Fenster verschieben. Die Standard-.slate-Konfigurationsdate auf Github zeigt einige Beispiele.

Meine persönliche Konfiguration in .slate lautete wie folgt:

~/.slate
# Um Tipparbeit zu sparen ctrl+shift+alt+cmd als 'hyperkey' definieren
alias hyperkey ctrl;shift;alt;cmd

# Schnellzugriffe auf Programme
# Holt mit einem Tastendruck ein bestimmtes (bereits geöffnetes)
# Programmfenster in den Vordergrund
bind w:${hyperkey} focus 'WhatsApp'
bind m:${hyperkey} focus 'Mail'
bind i:${hyperkey} focus 'Adobe InDesign CS5.5'

# Focus auf Fenster links/rechts
bind y:${hyperkey} focus right
bind v:${hyperkey} focus left

# Hint-Funktion: Hotkey-Overlay über jedes Fenster legen
bind esc:cmd hint

# Fenster auf den linken Bildschirm schieben
# und vergrößern
bind left:${hyperkey} throw 0 resize full

Eine recht simple und einfache Konfigurationsmöglichkeit, wenn man die Parameter mal überblickt hat. Natürlich ist auch noch viel mehr möglich, wie zum Beispiel die von Windowmanagern gewohnte Grid-Funktion oder ein Hotkey, der einen Buchstaben über jedes Fenster legt und drückt man noch den Buchstaben, holt es genau jenes Fenster in Vordergrund. So ist mausfreies Navigieren möglich.

Leider ist man, wenn man erst einmal Blut geleckt hat, in manchen Dingen auch etwas eingeschränkt. Mehr Möglichkeiten will Slate hier mit JavaScript ins Spiel bringen. Zum Beispiel nervt mich ein bestimmtes Programm, welches ich täglich einmal brauche und welches sein Hauptfenster immer möglichst klein öffnet, so dass man es erstmal größer ziehen muss, um etwas zu sehen.

Mit der JavaScript-Konfiguration .slate.js konnte ich dafür sorgen, dass jenes Fenster sofort vergrößert wird, sobald es sich öffnet.

~/.slate.js
slate.on("windowOpened", function(event, win) {
  if (win.app().name() === "smart.upload") {
    // Programmfenster mit dem Namen "smart.upload" im Titel hat sich geöffnet
    // Mache es etwas größer und schiebe es an Position 100/0
    win.doOperation(slate.operation("move", {"x" : "screenOriginX+100", "y" : "screenOriginY", "width" : "960", "height" : "1175"}));
    }
  });  

Allerdings steckt diese Sache mit dem JavaScript-Support noch etwas in den Kinderschuhen und die API-Dokumentation könnte etwas besser sein.
Daran, noch weitere Wünsche zu erfüllen (Fenster per Hotkey auf anderen Bildschirm schieben und in Vollbild öffnen oder Firefox-Popup-Fenster auf anderen Bildschirm schieben), bin ich leider gescheitert.

Auf der guten Seite

  • Für einen Windowmanager ist Slate wirklich sehr mächtig und bietet alles, was man von einem Standard-Windowmanager auch erwarten kann, wie 50/50-Fenster-Aufteilung und Grid-Funktion
  • Mit den Einzeilern in der Text-Konfigurationsdatei hat man schnell und einfach seine Wunsch-Funktion gebastelt
  • Jedes offene Fenster per Hotkey in Vordergrund holen (hint-Funktion)
  • Focus per Hotkey auf Fenster links oder rechts des aktuellen Fensters verlagern (focus-Funktion)

Auf der anderen Seiten

  • Stand April 2019 ist Slate in einem Umbruch von der Text-Konfiguration zur JavaScript-Konfiguration. Das heißt, für ersteres findet man schon nicht mehr so einfach die API-Dokumentation aber letzteres hat noch ein paar Kinderkrankheiten und ist meiner Ansicht nach API-mäßig auch nicht so ganz logisch aufgebaut
  • Debugging mittels macOS-Konsole

Fazit

Wer einen Windowmanager benötigt, der etwas mehr kann, als alle anderen, der ist mit Slate gut bedient.

Hammerspoon

OpenSource

Info:
https://www.hammerspoon.org/

Download:
https://github.com/Hammerspoon/hammerspoon/releases/latest

Dokumentation/API:
http://www.hammerspoon.org/docs/

 

Hammerspoon ist an sich kein Windowmanager sondern ein Automatisierungs-Tool. Da Hammerspoon aber volle Kontrolle über die macOS-GUI hat, kann man das Programm auch prima als Window-Manager einsetzen. Allerdings nicht mit einer einfachen GUI ("ich klicke den Haken an und habe gleich einen Hotkey, der meine Fenster aufteilt") und auch nicht mit einer simplen Text-Konfiguration wie bei Slate.
Hammerspoon setzt auf die Skriptsprache lua und erwartet für alles, was es tun muss, eine Skriptdatei namens init.lua im Verzeichnis ~/.hammerspoon/, in welchem sich außerdem noch Plugins, sogenannte "spoons", ablegen lassen.

Meine Konfiguration, mit welcher ich unter anderem das abdecken wollte, was ich kurz zuvor schon mit Slate erreicht hatte, sieht wie folgt aus:

~/.hammerspoon/init.lua
local wf = hs.window.filter -- Kürzel...benötigen wir später
local log = hs.logger.new('apfelz','debug') -- Falls man mal etwas ins Log schreiben möchte, erstellen wir hier eine Variable dafür

-- Kleine Testfunktion, um die beiden Benachrichtigungsarten
-- "alert" und "notify" zu sehen
-- Wird durch Tastenkürzel cmd+alt+ctrl+shift+t ausgelöst
hs.hotkey.bind({"cmd", "alt", "ctrl", "shift"}, "t", function()
  hs.alert.show("Hallo, Welt")
  hs.notify.new({title="Hammerspoon", informativeText="Hallo, Welt"}):send()
end)

-- Hilft beim Entwickeln:
-- Geometrie-Daten für aktuelles im Fokus befindliches Fenster anzeigen
-- Achtung bei mehreren Bildschirmen: Linke Kante ist erster Bildschirm!
hs.hotkey.bind({"cmd", "alt", "ctrl", "shift"}, "ß", function()
  local win  = hs.window.focusedWindow()
  local size = win:size()
  local pos  = win:topLeft()
  log.i(hs.inspect.inspect(pos))
    --log.i(string)              schreibt einen Info-Eintrag in das Log
    --hs.inspect.inspect(objekt) wandelt ein Objekt in einen logbaren String um
  hs.alert.show(win:title() .. "\n" .. math.floor(pos._x) .. "/" .. math.floor(pos._y) .. "\n" .. math.floor(size._w) .. "x" .. math.floor(size._h), 5)
end)

-- Aktuelles Fenster auf Vollbild schalten und auf Bildschirm nach links schieben
hs.hotkey.bind({"cmd", "alt", "ctrl", "shift"}, "Left", function()
  hs.window.focusedWindow():moveOneScreenWest()
  hs.window.focusedWindow():setFullScreen(true)
end)

-- Aktuelles Fenster auf Vollbild schalten und auf Bildschirm nach rechts schieben
hs.hotkey.bind({"cmd", "alt", "ctrl", "shift"}, "Right", function()
  hs.window.focusedWindow():moveOneScreenEast()
  hs.window.focusedWindow():setFullScreen(true)
end)

-- Hotkeys für spezielle Programme
-- Achtung: Nicht jedes Programm ist unter dem Namen erreichbar, der
-- zum Beispiel beim Hover im Dock zu erkennen ist.
-- Alle gerade offenen Programme können mit dem Befehl
-- hs.fnutils.each(hs.application.runningApplications(), function(app) print(app:title()) end)
-- mit ihrem Namen aufgelistet werden. Einfach diese Zeile in die Konsole kopieren
hs.hotkey.bind({"cmd", "alt", "ctrl", "shift"}, "w", function()
  local app = hs.appfinder.appFromName("WhatsApp")
  if app then app:activate() end
end)
hs.hotkey.bind({"cmd", "alt", "ctrl", "shift"}, "m", function()
  local app = hs.appfinder.appFromName("Mail")
  if app then app:activate() end
end)
hs.hotkey.bind({"cmd", "alt", "ctrl", "shift"}, "f", function()
  local app = hs.appfinder.appFromName("Firefox")
  if app then app:activate() end
end)
hs.hotkey.bind({"cmd", "alt", "ctrl", "shift"}, "i", function()
  local app = hs.appfinder.appFromName("InDesign")
  if app then app:activate() end
end)

-- Zu kleines Fenster eines bestimmten nervigen Programmes vergrößern,
-- sobald es geöffnet wird
wf_uploader = wf.new(false):setAppFilter('smart.upload')
wf_uploader:subscribe(wf.windowCreated, function(win)
  if win ~= nil then
    -- Die Geometrie-Werte sind hier Prozent-Werte!
    win:move(hs.geometry({x=0.05, y=0, w=0.5, h=1}))
    end
end)

-- FileZilla macht denselben Mist!
-- Zu kleines FileZilla-Fenster vergrößern
wf_filezilla = wf.new(false):setAppFilter('FileZilla')
wf_filezilla:subscribe(wf.windowCreated, function(win)
  if win ~= nil and win:title() == "FileZilla" then
    win:move(hs.geometry({x=0.05, y=0, w=0.7, h=1}))
  elseif win ~= nil and win:title() == "Servermanager" then
    win:move(hs.geometry({x=0.3, y=0.05, w=0.5, h=0.9}))
  end
end)

-- Zwei nervige Popup-Fenster des Browsers
-- wegschieben und automatisch schließen
wf_firefox = wf.new(false):setAppFilter('Firefox')
  -- Man könnte hier gleich auf den Fenstertitel filtern, allerdings ist
  -- der Titel zu dieser Zeit immer "Mozilla Firefox" und nicht <title/>-Attribut
  -- der Website, weil die Website erst noch geladen wird!
wf_firefox:subscribe(wf.windowCreated, function(win)
  --log.i(win:title()) -- Titel ist immer "Mozilla Firefox"!
  --log.i(hs.inspect.inspect(win:size()))
  local size = win:size()
  -- Das ist mit der Größe 550x455 sehr sicher das nervige Download-Fenster
  -- welches absolut im Weg ist, ich aber noch nicht schließen kann
  if size._w == 550 and size._h == 455 then
    win:moveOneScreenEast()
    win:move(hs.geometry(720,390))
    end
  -- Das ist sehr sicher das Pocket-Fenster, wenn ich einen Artikel
  -- hinzugefügt habe. In die untere Bildschirmecke schieben und
  -- nach 5 Sekunden schließen
  if size._w == 500 and size._h == 455 then
    --log.i(win:title()) -- Titel ist immer "Mozilla Firefox"!
    win:move(hs.geometry(1160,560))
    hs.timer.doAfter(5, function() -- Nach 5 Sekunden schließen
      -- Inzwischen ist der Titel des Fensters geladen
      -- Zur Sicherheit nochmals nachschauen, ob es sich auch tatsächlich
      -- um das Fenster handelt, welches ich nicht benötige
      if win:title() == "Pocket: Element hinzufügen" then
        win:close()
        end
      end)
    end
end)

-- Nach einem Neustart wird die Mail-Vorschau "Herald" auf dem falschen Bildschirm dargestellt
-- Beim ersten Erscheinen auf dem falschen Bildschirm zum richtigen rüberschieben
wf_herald = wf.new(false):setAppFilter('Herald')
wf_herald:subscribe(wf.windowCreated, function(win)
  local pos  = win:topLeft()
  --log.i(pos._x)
  if pos._x > 1460 then
    --win:moveOneScreenEast() -- Funktioniert hier nicht!
    win:move(hs.geometry((1460-pos._x),0))
    end
  end)

Auf der guten Seite

  • Beim Rumspielen mit Hammerspoon ist mir nichts aufgefallen, was man mit diesem Automatisierungstool nicht erschlagen könnte. Etwas Inspiration kann man sich hier holen
  • Loslegen und Entwickeln ist bei Hammerspoon einfacher, als bei Slate: Ãœber das Menü-Icon kann man direkt die Config-Datei init.lua (die muss man bei Slate erstmal manuell finden) und eine Konsole öffnen, bei welcher man zum Testen auch Einzeiler absenden kann. So etwas hatte ich bei Slate gleich zu Anfang vermisst

Auf der anderen Seite
  • Für meinen Einsatz-Zweck eines Windowmanagers wird hier vielleicht mit Kanonen auf Spatzen geschossen ;-)