Linux window auto-organizer (across multiple screens)
One of the things that always makes me crazy is window organization on the desktop. OSX has pretty good apps to manage windows, but i always find Linux a bit lacking so i decided to roll out my own simple solution.
It's written in Elixir because i like it, but can easily be adapter for other languages since it uses wmctrl to get information about windows and define their positions. The rest is just find the windows roughly by name and assigning the positions and dimensions i want. And it works across multiple screens because apparently X11 reports positions as one single virtual screen if you are not mirroring your screens, of course.
Just put the script in a directory that your PATH contains, and don't forget to change the first line to point to the correct elixir binary path. Then you can just execute whenever you want and all your windows will pop into their positions automagically.
Happy windowing...
EDIT: Updated script to not blow up when a particular window is not open.
#!/home/void/.asdf/shims/elixir require Logger windows = %{ opera: %{x: 3408, y: 0, w: 1712, h: 1403}, terminal: %{x: 0, y: 0, w: 1584, h: 1371}, thunderbird: %{x: 6045, y: 200, w: 995, h: 1051}, jetbrains: %{x: 2560, y: 0, w: 2060, h: 1373}, slack: %{x: 5120, y: 200, w: 1184, h: 1050}, spotify: %{x: 1592, y: 0, w: 968, h: 1373} } defmodule WindowGatherer do def run(windows) do IO.inspect("Gathering windows...") {window_lines, 0} = System.cmd("wmctrl", ["-l", "-p", "-G", "-x"]) window_lines = window_lines |> String.split("\n") |> Enum.reject(&(String.split(&1) == [])) |> Enum.map(fn line -> [id, _, _, _, _, _, _ | rest] = String.split(line) %{ id: id, names: rest |> Enum.join(" ") |> String.downcase() } end) fill_ids(windows, window_lines) end defp fill_ids(windows, window_lines) do windows |> Enum.map(fn {window_key, window_data} -> window_attributes = window_lines |> Enum.filter(&(&1.names =~ Atom.to_string(window_key))) |> Enum.reject(&is_nil/1) |> Enum.at(0) case window_attributes do map when is_map(window_attributes) -> {window_key, Map.put(window_data, :id, Map.fetch!(map, :id))} _ -> nil end end) |> Enum.reject(&is_nil/1) |> Map.new() end end defmodule WindowOrganizer do def run(windows) do IO.inspect("Organizing windows...") windows |> Enum.each(fn {key, win} -> IO.inspect("Moving #{key}...") System.cmd("wmctrl", [ "-ir", win.id, "-e", "0,#{win.x},#{win.y},#{win.w},#{win.h}" ]) end) end end windows |> WindowGatherer.run() |> WindowOrganizer.run()