diff --git a/bin/i3_empty_workspace.sh b/bin/i3_empty_workspace.sh
new file mode 100755
index 0000000..b4c28dc
--- /dev/null
+++ b/bin/i3_empty_workspace.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+MAX_DESKTOPS=20
+
+WORKSPACES=$(seq -s '\n' 1 1 ${MAX_DESKTOPS})
+
+EMPTY_WORKSPACE=$( (i3-msg -t get_workspaces | tr ',' '\n' | grep num | awk -F: '{print int($2)}' ; \
+ echo -e ${WORKSPACES} ) | sort -n | uniq -u | head -n 1)
+
+i3-msg workspace ${EMPTY_WORKSPACE}
diff --git a/bin/i3_reconfigure b/bin/i3_reconfigure
new file mode 100755
index 0000000..a3a045a
--- /dev/null
+++ b/bin/i3_reconfigure
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+pushd ~/.dotfiles/i3/ &> /dev/null
+python3 binds.py > config
+i3-msg reload
diff --git a/bin/i3_send_to_workspace.sh b/bin/i3_send_to_workspace.sh
new file mode 100755
index 0000000..ac960dc
--- /dev/null
+++ b/bin/i3_send_to_workspace.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+function gen_workspaces()
+{
+ i3-msg -t get_workspaces | tr ',' '\n' | grep "name" | sed 's/"name":"\(.*\)"/\1/g' | sort -n
+}
+
+
+WORKSPACE=$( gen_workspaces | rofi -dmenu -i -p "Select workspace:")
+
+if [ -n "${WORKSPACE}" ]
+then
+ i3-msg move container to workspace "${WORKSPACE}"
+fi
diff --git a/bin/i3_switch_workspace.sh b/bin/i3_switch_workspace.sh
new file mode 100755
index 0000000..ab1e304
--- /dev/null
+++ b/bin/i3_switch_workspace.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+function gen_workspaces()
+{
+ i3-msg -t get_workspaces | tr ',' '\n' | grep "name" | sed 's/"name":"\(.*\)"/\1/g' | sort -n
+}
+
+
+WORKSPACE=$( (echo empty; gen_workspaces) | rofi -dmenu -i -p "Select workspace:")
+
+if [ x"empty" = x"${WORKSPACE}" ]
+then
+ i3_empty_workspace.sh
+elif [ -n "${WORKSPACE}" ]
+then
+ i3-msg workspace "${WORKSPACE}"
+fi
diff --git a/compton.conf b/compton.conf
new file mode 100644
index 0000000..dc62a3f
--- /dev/null
+++ b/compton.conf
@@ -0,0 +1,146 @@
+# This is my compton configuration after a quick cleanup. (It's still none too organized; sorry about that.)
+# With this file at ~/.config/compton.conf, I can run compton without any arguments (just plain `compton`).
+#
+# In the hopes that explaining my software and hardware environment might be helpful to you:
+#
+# I use this configuration on Ubuntu 15.10 (and have used it on previous releases); I am currently using the 352.63 ("long-lived
+# branch") NVIDIA binary drivers, installed from the Ubuntu software repositories. I use fluxbox as my window manager; most of
+# the other components of my desktop environment are borrowed from Xfce.
+#
+# My workstation at home has an i7-4930K and a GTX 970 in it, which are together more than enough to drive several 4K displays
+# without tearing or lagging. Even with quite a few windows open at the moment, 'nvidia-smi' shows the X.org server using
+# about 600 MiB of video memory; both the CPU and the GPU are effectively idle.
+#
+# I use the version of compton packaged in my PPA (https://launchpad.net/~kelleyk/+archive/ubuntu/compton). As of the time of this
+# writing, that is d7f95b5, which is the what's on the master branch of the upstream compton repository.
+#
+# My X11 configuration is very "normal". On 15.04, I did have the following in a file in /etc/X11/xorg.conf.d/; I have not
+# investigated yet, but I haven't noticed any ill effects from *not* having it since installing 15.10 a few hours ago.
+# In some cases, UseEvents can make things unstable (though I haven't run into that in a long time); however, not enabling
+# it will cause the X server to spin (leading to high CPU usage) while waiting for certain resources.
+# ---------------------------
+# Section "Device"
+# Identifier "Device0"
+# Option "UseEvents" "True"
+# EndSection
+# ---------------------------
+#
+# Good luck!
+
+# References:
+# - https://github.com/chjj/compton/wiki
+# - http://ubuntuforums.org/showthread.php?t=2144468
+
+### Backend/performance options
+backend = "glx";
+paint-on-overlay = true;
+glx-no-stencil = true;
+glx-no-rebind-pixmap = true;
+
+# (TODO: Clean up my notes about selecting a vsync implementation.)
+xrender-sync-fence = true;
+vsync = "opengl-swc";
+
+# This option throttles refresh rates. Not compatible with vsync=drm/opengl/opengl-oml.
+# (Note: for me, enabling this just makes the choppy dragging more noticeable, like the update rate has gone down.)
+sw-opti = true;
+
+# # Per compton performance tips (from the GitHub wiki), only one of these three
+# # (glx-use-copysubbuffermesa, glx-copy-from-front, glx-swap-method)
+# # should be used.
+# glx-use-copysubbuffermesa = true;
+# glx-copy-from-front = false;
+# glx-swap-method = "undefined";
+glx-swap-method = "exchange"; # requires "allow flipping" in nvidia-settings
+
+### Shadow
+shadow = true;
+no-dnd-shadow = true;
+no-dock-shadow = true;
+clear-shadow = true;
+shadow-radius = 7;
+shadow-offset-x = -7;
+shadow-offset-y = -7;
+shadow-opacity = 0.5;
+# shadow-red = 0.0;
+# shadow-green = 0.0;
+# shadow-blue = 0.0;
+shadow-exclude = [
+ # From the Ubuntu forums link ('screaminj3sus')
+ "! name~=''",
+ "n:e:Notification",
+ "n:e:Plank",
+ "n:e:Docky",
+ "g:e:Synapse",
+ "g:e:Kupfer",
+ "g:e:Conky",
+ "n:w:*Firefox*",
+ "n:w:*Chrome*",
+ "n:w:*Chromium*",
+ "class_g ?= 'Notify-osd'",
+ "class_g ?= 'Cairo-dock'",
+ "class_g ?= 'Xfce4-notifyd'",
+ "class_g ?= 'Xfce4-power-manager'"
+];
+shadow-ignore-shaped = false;
+# shadow-exclude-reg = "x10+0+0";
+# xinerama-shadow-crop = true;
+
+### Opacity
+menu-opacity = 0.95;
+# inactive-opacity = 0.85;
+# active-opacity = 0.8;
+frame-opacity = 0.50; # i.e. titlebars, borders
+inactive-opacity-override = false;
+alpha-step = 0.06;
+opacity-rule = [ "90:class_g = 'kitty'" ];
+
+# inactive-dim = 0.2;
+# inactive-dim-fixed = true;
+
+### Blur options
+# blur-background = true;
+# blur-background-frame = true;
+# blur-kern = "3x3box"
+# blur-kern = "5,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1"
+# blur-background-fixed = true;
+blur-background-exclude = [ "window_type = 'dock'", "window_type = 'desktop'" ];
+
+### Fading
+fading = true;
+fade-delta = 5; # 30;
+fade-in-step = 0.03;
+fade-out-step = 0.03;
+# no-fading-openclose = true;
+fade-exclude = [ ];
+
+### Other
+
+mark-wmwin-focused = true;
+mark-ovredir-focused = true;
+
+# Use EWMH _NET_WM_ACTIVE_WINDOW to determine which window is focused instead of using FocusIn/Out events.
+# Usually more reliable but depends on a EWMH-compliant WM.
+use-ewmh-active-win = true;
+
+# # Detect rounded corners and treat them as rectangular when --shadow-ignore-shaped is on.
+# detect-rounded-corners = true;
+
+detect-client-opacity = true;
+refresh-rate = 0;
+dbe = false;
+unredir-if-possible = true;
+# unredir-if-possible-delay = 5000;
+# unredir-if-possible-exclude = [ ];
+# focus-exclude = [ "class_g = 'Cairo-clock'" ];
+detect-transient = true;
+detect-client-leader = true;
+invert-color-include = [ ];
+# resize-damage = 1;
+
+# Window type settings
+wintypes:
+{
+ tooltip = { fade = true; shadow = false; opacity = 0.75; focus = true; };
+};
+
diff --git a/deploy.py b/deploy.py
index 5e974e3..de3b699 100644
--- a/deploy.py
+++ b/deploy.py
@@ -22,6 +22,8 @@ targeted = {
"doom": ".doom.d",
"fish": ".config/fish",
"alacritty": ".config/alacritty",
+ "i3": ".config/i3",
+ "compton.conf": ".config/compton.conf",
}
diff --git a/i3/binds.py b/i3/binds.py
new file mode 100755
index 0000000..503a9ae
--- /dev/null
+++ b/i3/binds.py
@@ -0,0 +1,545 @@
+# A script to generate keybindings for i3 Window Manager modes with consistent behavior and help text.
+from __future__ import annotations
+
+import io
+from contextlib import redirect_stdout
+from dataclasses import dataclass, field
+from pathlib import Path
+from textwrap import indent
+from typing import List, Optional, Dict, Tuple
+
+
+@dataclass
+class Binding:
+ """An abstract superclass for both mode and command bindings."""
+
+ name: str
+ key: str = field(default="")
+
+ # Execute the binding only on key release
+ release: bool = field(default=False)
+
+ # A hidden binding won't show up in mode descriptions. Useful if you want to have
+ # a second version of a binding with a modifier key, but don't need it to show up separately."""
+ hidden: bool = field(default=False)
+ # Override the binding's default hint text.
+ hint: str = field(init=False, default="")
+
+ def __post_init__(self):
+ if not self.key:
+ self.key = self.name.lower()[0]
+
+ def binding(self, in_mode: Optional[str] = None) -> str:
+ pass
+
+ def _bind_prefix(self, key: Optional[str] = None) -> str:
+ prefix = "bindsym "
+ if self.release:
+ prefix += "--release "
+ prefix += key or self.key
+ return prefix
+
+ def mode_hint(self) -> str:
+ """How to display this binding in the help text of a Mode."""
+ if self.hint:
+ return self.hint
+
+ if len(self.key) == 1 and self.key in self.name:
+ return self.name.replace(self.key, f"{self.key}", 1)
+
+ return f"[{self.key}]{self.name}"
+
+
+@dataclass
+class Mode(Binding):
+ """A mode which can contain a list of sub-bindings, which may be a mix of commands and modes.
+ exit_bindings contains a list of binds that will cancel the mode and return to default."""
+
+ bindings: List[Binding] = field(default_factory=list)
+
+ exit_bindings = [
+ "Return",
+ "Escape",
+ "control+g",
+ "control+bracketleft",
+ ]
+
+ def __post_init__(self):
+ super().__post_init__()
+
+ used_keys: Dict[str, Binding] = {}
+ for binding in self.bindings:
+ # Deconflicting directional bindings is complicated, so let's punt on it for now
+ if isinstance(binding, Directional):
+ continue
+ err_base = (
+ f"Binding {binding.key} for {self.name}.{binding.name} conflicts with "
+ )
+ if binding.key in self.exit_bindings:
+ raise ValueError(err_base + "mode escape key.")
+ if binding.key in used_keys:
+ raise ValueError(err_base + used_keys[binding.key].name)
+ used_keys[binding.key] = binding
+
+ def mode_name(self, parent: Optional[str]) -> str:
+ """The full name of the mode including its parents names, for disambiguation."""
+ if parent:
+ return f"{parent}_{self.name}"
+ else:
+ return self.name
+
+ def mode_var(self, parent: Optional[str]) -> str:
+ """The name of the variable representing the mode."""
+ return f"$mode_{self.mode_name(parent)}"
+
+ def mode_label(self, parent: Optional[str]) -> str:
+ """The mode label to show at the start of the mode help text."""
+ lineage = self.mode_name(parent).split("_")
+
+ # Remove the common [space] prefix on nested modes.
+ if len(lineage) > 1:
+ lineage.remove("space")
+ return "".join(f"[{part}]" for part in lineage)
+
+ def mode_def(self, parent: Optional[str]) -> str:
+ name = self.mode_name(parent)
+ var = self.mode_var(parent)
+
+ binding_names = ", ".join(
+ [binding.mode_hint() for binding in self.bindings if not binding.hidden]
+ )
+ help_text = f"{self.mode_label(parent)}: {binding_names}"
+
+ mode = f"set {var} {help_text}\n" f'mode --pango_markup "{var}" {{\n'
+
+ for binding in self.bindings:
+ mode += indent(binding.binding(name) + "\n", " ")
+
+ for binding in self.exit_bindings:
+ mode += indent(f'{self._bind_prefix(binding)} mode "default"\n', " ")
+
+ mode += "}\n"
+
+ submodes = [binding for binding in self.bindings if isinstance(binding, Mode)]
+
+ for sub in submodes:
+ mode += "\n"
+ mode += sub.mode_def(name)
+
+ return mode
+
+ def binding(self, parent: Optional[str] = None) -> str:
+ return f'{self._bind_prefix()} mode "{self.mode_var(parent)}"'
+
+
+@dataclass
+class Command(Binding):
+ command: Optional[str] = field(default=None)
+ exit_mode: bool = field(default=True)
+
+ def __post_init__(self):
+ super().__post_init__()
+ if not self.command:
+ self.command = self.name.lower()
+
+ def binding(self, parent: Optional[str] = None) -> str:
+ return self._binding(self.key, self.command, parent)
+
+ def _binding(self, key: str, command: str, parent: Optional[str] = None) -> str:
+ bind = f"{self._bind_prefix(key)} {command}"
+ if parent and self.exit_mode:
+ bind += ', mode "default"'
+ return bind
+
+
+@dataclass
+class Exec(Command):
+ startup_id: bool = field(default=False)
+
+ def binding(self, parent: Optional[str] = None) -> str:
+ return self._binding(
+ self.key,
+ f"exec {'--no-startup-id ' if not self.startup_id else ''}{self.command}",
+ parent,
+ )
+
+
+@dataclass
+class Directional(Command):
+ """A convenience class for directional commands that can be executed upleft/down/up/right with h/j/k/l or the
+ arrow keys. Optional parameters include a subset of directions to use, and a modifier for the command.
+ The command should include the template variable {direction}."""
+
+ subset: Optional[List[str]] = None
+ modifier: Optional[str] = None
+ flip: bool = False
+
+ flips = {"left": "right", "right": "left", "down": "up", "up": "down"}
+
+ directions = {
+ "left": ["h", "Left"],
+ "down": ["j", "Down"],
+ "up": ["k", "Up"],
+ "right": ["l", "Right"],
+ }
+
+ def prefix(self) -> str:
+ return self.modifier + "+" if self.modifier else ""
+
+ def used_keys(self) -> Tuple[List[str], List[str]]:
+ letter_keys = []
+ arrow_keys = []
+
+ for direction in self.directions:
+ if not self.subset or direction in self.subset:
+ letter, arrow = self.directions[direction]
+ letter_keys.append(letter)
+ arrow_keys.append(arrow)
+
+ return letter_keys, arrow_keys
+
+ def mode_hint(self) -> str:
+ letter_keys, arrow_keys = self.used_keys()
+
+ hint = self.prefix() + ""
+
+ hint += "/".join(letter_keys)
+ hint += "/"
+ hint += "/".join(arrow_keys)
+
+ hint += f" {self.name}"
+ return hint
+
+ def binding(self, parent: Optional[str] = None):
+ bind = ""
+ for direction, keys in self.directions.items():
+ if not self.subset or direction in self.subset:
+ if self.flip:
+ direction = self.flips[direction]
+ for key in keys:
+ bind += self._binding(self.prefix() + key, self.command).format(
+ direction=direction
+ )
+ bind += "\n"
+ return bind
+
+
+@dataclass
+class App:
+ """
+ Required args:
+ name: The name of the app.
+ Accepted args:
+ key: The key to use. Defaults to the first lower case letter of the name.
+ Regular press will try to switch to the app if it's already open.
+ Shift press will guarantee opening a new instance. Shift bindings are hidden from mode help text.
+
+ path: The binary to execute. Defaults to the lowercase version of the name.
+ args: Args to pass to the app on launch
+ class: The X11 Window Class to search for. Defaults to the app name. Case insensitive.
+ title: The X11 Window Title to search for.
+ switch: If True, switch to the app instead of launching a new instance unless shift is held. Default True.
+ """
+
+ name: str
+ key: str = field(default="")
+ path: str = field(default="")
+ args: str = field(default="")
+ window_class: str = field(default="")
+ window_title: str = field(default="")
+ switch: bool = field(default=True)
+
+ def __post_init__(self):
+ if not self.key:
+ self.key = self.name.lower()[0]
+ if not self.path:
+ self.path = self.name.lower()
+ if not self.window_class:
+ self.window_class = self.name
+
+ @property
+ def class_query(self) -> str:
+ return f'class="(?i){self.window_class}"'
+
+ @property
+ def title_query(self) -> str:
+ if self.window_title:
+ return f' title="(?i){self.window_title}"'
+ else:
+ return ""
+
+ @property
+ def query(self) -> str:
+ return f"[{self.class_query}{self.title_query}]"
+
+ def commands(self) -> List[Command]:
+ cmd = f"{self.path} {self.args}"
+ switch_cmd = f"pgrep {self.path} && i3-msg '{self.query} focus' || {cmd}"
+
+ commands = [
+ Exec(self.name, key=self.key, command=switch_cmd if self.switch else cmd),
+ Exec(self.name, key=f"Shift+{self.key}", command=cmd, hidden=True),
+ ]
+
+ return commands
+
+ @staticmethod
+ def find(name: str) -> Optional[App]:
+ for app in apps:
+ if app.name == name:
+ return app
+ return None
+
+
+apps = [
+ App("firefox"),
+ App("kitty", key="t", args="--single-instance", switch=False),
+ App("emacs"),
+ App("pycharm"),
+ App("idea", key="j"),
+ App("discord"),
+ App("slack"),
+ App("zoom"),
+ App(
+ "windows",
+ path="virt-viewer",
+ args="--connect qemu:///system -w Windows10",
+ window_class="virt-viewer",
+ ),
+]
+
+bindings = [
+ Exec("Terminal", key="$mod+Return", command="kitty --single-instance"),
+ Command("next workspace", key="$mod+Tab", command="workspace next_on_output"),
+ Command("prev workspace", key="$mod+Shift+Tab", command="workspace prev_on_output"),
+ # Volume control
+ Exec(
+ "Volume Up",
+ key="XF86AudioRaiseVolume",
+ command="pactl set-sink-volume @DEFAULT_SINK@ +10%",
+ startup_id=False,
+ ),
+ Exec(
+ "Volume Down",
+ key="XF86AudioLowerVolume",
+ command="pactl set-sink-volume @DEFAULT_SINK@ -10%",
+ startup_id=False,
+ ),
+ Exec(
+ "Mute",
+ key="XF86AudioMute",
+ command="pactl set-sink-mute @DEFAULT_SINK@ toggle",
+ startup_id=False,
+ ),
+ Directional("Focus", command="focus {direction}", modifier="$mod"),
+ Directional(
+ "Move", command="move {direction}", modifier="$mod+Shift", release=True
+ ),
+ Mode(
+ "space",
+ key="$mod+space",
+ bindings=[
+ Exec("Run", key="space", command="rofi -show run"),
+ Mode(
+ "workspace",
+ key="p",
+ bindings=[
+ Mode(
+ "move",
+ bindings=[
+ Directional(
+ "Move",
+ command="exec --no-startup-id i3-msg move workspace to output {direction}",
+ subset=["left", "right"],
+ exit_mode=False,
+ ),
+ ],
+ ),
+ Exec("switch to", key="p", command="i3_switch_workspace.sh"),
+ ],
+ ),
+ Mode(
+ "window",
+ bindings=[
+ Command("delete", command="kill"),
+ Command("fullscreen", command="fullscreen toggle"),
+ Command("float", key="o", command="floating toggle"),
+ Command("split", command="split h"),
+ Command("vertical split", key="v", command="split v"),
+ Command("parent", command="focus parent", exit_mode=False),
+ Mode(
+ "move",
+ bindings=[
+ Directional(
+ "Move",
+ command="move {direction}",
+ exit_mode=False,
+ release=True,
+ ),
+ Exec(
+ "to workspace",
+ key="p",
+ command="i3_send_to_workspace.sh",
+ ),
+ ],
+ ),
+ Mode(
+ "resize",
+ bindings=[
+ Directional(
+ "Grow",
+ command="resize grow {direction} 10 px or 10 ppt",
+ exit_mode=False,
+ ),
+ Directional(
+ "Shrink",
+ command="resize shrink {direction} 10 px or 10 ppt",
+ modifier="Shift",
+ exit_mode=False,
+ flip=True,
+ ),
+ ],
+ ),
+ Mode(
+ "layout",
+ bindings=[
+ Command("split", command="toggle split"),
+ Command("tabbed", command="layout tabbed"),
+ Command("stacking", key="k", command="layout stacking"),
+ ],
+ ),
+ ],
+ ),
+ Mode("open", bindings=[cmd for app in apps for cmd in app.commands()]),
+ Mode(
+ "goto",
+ bindings=[
+ Exec("server", command="rofi -show ssh"),
+ ],
+ ),
+ Mode(
+ "quit",
+ bindings=[
+ Exec("logout", key="q", command="i3-msg exit"),
+ Exec("reload", command="i3_reconfigure", key="r"),
+ Command("restart", key="Shift+r"),
+ Mode(
+ "system",
+ bindings=[
+ Exec(
+ "suspend", startup_id=False, command="systemctl suspend"
+ ),
+ Exec(
+ "reboot", startup_id=False, command="systemctl reboot"
+ ),
+ Exec(
+ "power off",
+ startup_id=False,
+ command="systemctl poweroff",
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+]
+
+
+@dataclass
+class Output:
+ name: str
+ pos: str
+ mode: str = field(default="2560x1440")
+ rate: int = field(default=60)
+ primary: bool = field(default=False)
+
+ def xrandr_flags(self) -> str:
+ flags = f"--output {self.name} --pos {self.pos} --mode {self.mode} --rate {self.rate}"
+ if self.primary:
+ flags += " --primary"
+ return flags
+
+
+outputs = [
+ Output("DP-0", "0x0"),
+ Output("DP-4", "2560x0", rate=144, primary=True),
+ Output("HDMI-0", "5120x0"),
+]
+
+
+def xrandr_command() -> str:
+ cmd = "exec --no-startup-id xrandr\\\n"
+ for output in outputs:
+ cmd += indent(output.xrandr_flags() + " \\\n", " ")
+ cmd = cmd[:-3]
+ return cmd
+
+
+@dataclass
+class Workspace:
+ name: str
+ output: str = field(default="primary")
+ assigned_apps: List[str] = field(default_factory=list)
+
+ def config(self, index: int) -> str:
+ ws_var = f"$ws_{self.name.lower()}"
+ cfg = f"set {ws_var} {index}: {self.name}\n"
+ cfg += f'workspace "{ws_var}" output {self.output}\n'
+ for a in self.assigned_apps:
+ app = App.find(a)
+ if not app:
+ raise ValueError(f"Couldn't find app {a}")
+ cfg += f'assign {app.query} "{ws_var}"\n'
+
+ return cfg
+
+
+workspaces = [
+ Workspace("Main", output="DP-4"),
+ Workspace("Tasks", output="DP-0", assigned_apps=["windows"]),
+ Workspace("Comms", output="HDMI-0", assigned_apps=["discord", "slack"]),
+ Workspace("Python", output="primary", assigned_apps=["pycharm"]),
+ Workspace("Java", output="primary", assigned_apps=["idea"]),
+]
+
+
+def workspace_config() -> str:
+ f = io.StringIO()
+
+ with redirect_stdout(f):
+ for idx, workspace in enumerate(workspaces):
+ print(workspace.config(idx + 1))
+
+ return f.getvalue()
+
+
+def binds_config() -> str:
+ f = io.StringIO()
+
+ preamble = (Path(__file__).parent / "preamble").read_text()
+ postscript = (Path(__file__).parent / "postscript").read_text()
+
+ with redirect_stdout(f):
+ print(preamble)
+ print()
+ print(xrandr_command())
+ print()
+ for binding in bindings:
+ if isinstance(binding, Mode):
+ print(binding.mode_def(None))
+ print(binding.binding())
+
+ print()
+ print(workspace_config(), end="")
+ print(postscript)
+
+ return f.getvalue()
+
+
+def main():
+ print(binds_config())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/i3/config b/i3/config
new file mode 100644
index 0000000..5add964
--- /dev/null
+++ b/i3/config
@@ -0,0 +1,246 @@
+set $mod Mod4
+set $alt Mod1
+
+# Use Mouse+$mod to drag floating windows to their wanted position
+floating_modifier $alt
+
+# Font for window titles. Will also be used by the bar unless a different font
+# is used in the bar {} block below.
+font pango:DejaVu Sans Mono 10
+
+exec --no-startup-id compton
+
+
+exec --no-startup-id xrandr\
+ --output DP-0 --pos 0x0 --mode 2560x1440 --rate 60 \
+ --output DP-4 --pos 2560x0 --mode 2560x1440 --rate 144 --primary \
+ --output HDMI-0 --pos 5120x0 --mode 2560x1440 --rate 60
+
+bindsym $mod+Return exec --no-startup-id kitty --single-instance
+bindsym $mod+Tab workspace next_on_output
+bindsym $mod+Shift+Tab workspace prev_on_output
+bindsym XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ +10%
+bindsym XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ -10%
+bindsym XF86AudioMute exec --no-startup-id pactl set-sink-mute @DEFAULT_SINK@ toggle
+bindsym $mod+h focus left
+bindsym $mod+Left focus left
+bindsym $mod+j focus down
+bindsym $mod+Down focus down
+bindsym $mod+k focus up
+bindsym $mod+Up focus up
+bindsym $mod+l focus right
+bindsym $mod+Right focus right
+
+bindsym --release $mod+Shift+h move left
+bindsym --release $mod+Shift+Left move left
+bindsym --release $mod+Shift+j move down
+bindsym --release $mod+Shift+Down move down
+bindsym --release $mod+Shift+k move up
+bindsym --release $mod+Shift+Up move up
+bindsym --release $mod+Shift+l move right
+bindsym --release $mod+Shift+Right move right
+
+set $mode_space [space]: [space]Run, workspace, window, open, goto, quit
+mode --pango_markup "$mode_space" {
+ bindsym space exec --no-startup-id rofi -show run, mode "default"
+ bindsym p mode "$mode_space_workspace"
+ bindsym w mode "$mode_space_window"
+ bindsym o mode "$mode_space_open"
+ bindsym g mode "$mode_space_goto"
+ bindsym q mode "$mode_space_quit"
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+ bindsym control+g mode "default"
+ bindsym control+bracketleft mode "default"
+}
+
+set $mode_space_workspace [workspace]: move, [p]switch to
+mode --pango_markup "$mode_space_workspace" {
+ bindsym m mode "$mode_space_workspace_move"
+ bindsym p exec --no-startup-id i3_switch_workspace.sh, mode "default"
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+ bindsym control+g mode "default"
+ bindsym control+bracketleft mode "default"
+}
+
+set $mode_space_workspace_move [workspace][move]: h/l/Left/Right Move
+mode --pango_markup "$mode_space_workspace_move" {
+ bindsym h exec --no-startup-id i3-msg move workspace to output left
+ bindsym Left exec --no-startup-id i3-msg move workspace to output left
+ bindsym l exec --no-startup-id i3-msg move workspace to output right
+ bindsym Right exec --no-startup-id i3-msg move workspace to output right
+
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+ bindsym control+g mode "default"
+ bindsym control+bracketleft mode "default"
+}
+
+set $mode_space_window [window]: delete, fullscreen, float, split, vertical split, parent, move, resize, layout
+mode --pango_markup "$mode_space_window" {
+ bindsym d kill, mode "default"
+ bindsym f fullscreen toggle, mode "default"
+ bindsym o floating toggle, mode "default"
+ bindsym s split h, mode "default"
+ bindsym v split v, mode "default"
+ bindsym p focus parent
+ bindsym m mode "$mode_space_window_move"
+ bindsym r mode "$mode_space_window_resize"
+ bindsym l mode "$mode_space_window_layout"
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+ bindsym control+g mode "default"
+ bindsym control+bracketleft mode "default"
+}
+
+set $mode_space_window_move [window][move]: h/j/k/l/Left/Down/Up/Right Move, to workspace
+mode --pango_markup "$mode_space_window_move" {
+ bindsym --release h move left
+ bindsym --release Left move left
+ bindsym --release j move down
+ bindsym --release Down move down
+ bindsym --release k move up
+ bindsym --release Up move up
+ bindsym --release l move right
+ bindsym --release Right move right
+
+ bindsym p exec --no-startup-id i3_send_to_workspace.sh, mode "default"
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+ bindsym control+g mode "default"
+ bindsym control+bracketleft mode "default"
+}
+
+set $mode_space_window_resize [window][resize]: h/j/k/l/Left/Down/Up/Right Grow, Shift+h/j/k/l/Left/Down/Up/Right Shrink
+mode --pango_markup "$mode_space_window_resize" {
+ bindsym h resize grow left 10 px or 10 ppt
+ bindsym Left resize grow left 10 px or 10 ppt
+ bindsym j resize grow down 10 px or 10 ppt
+ bindsym Down resize grow down 10 px or 10 ppt
+ bindsym k resize grow up 10 px or 10 ppt
+ bindsym Up resize grow up 10 px or 10 ppt
+ bindsym l resize grow right 10 px or 10 ppt
+ bindsym Right resize grow right 10 px or 10 ppt
+
+ bindsym Shift+h resize shrink right 10 px or 10 ppt
+ bindsym Shift+Left resize shrink right 10 px or 10 ppt
+ bindsym Shift+j resize shrink up 10 px or 10 ppt
+ bindsym Shift+Down resize shrink up 10 px or 10 ppt
+ bindsym Shift+k resize shrink down 10 px or 10 ppt
+ bindsym Shift+Up resize shrink down 10 px or 10 ppt
+ bindsym Shift+l resize shrink left 10 px or 10 ppt
+ bindsym Shift+Right resize shrink left 10 px or 10 ppt
+
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+ bindsym control+g mode "default"
+ bindsym control+bracketleft mode "default"
+}
+
+set $mode_space_window_layout [window][layout]: split, tabbed, stacking
+mode --pango_markup "$mode_space_window_layout" {
+ bindsym s toggle split, mode "default"
+ bindsym t layout tabbed, mode "default"
+ bindsym k layout stacking, mode "default"
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+ bindsym control+g mode "default"
+ bindsym control+bracketleft mode "default"
+}
+
+set $mode_space_open [open]: firefox, kitty, emacs, pycharm, [j]idea, discord, slack, zoom, windows
+mode --pango_markup "$mode_space_open" {
+ bindsym f exec --no-startup-id pgrep firefox && i3-msg '[class="(?i)firefox"] focus' || firefox , mode "default"
+ bindsym Shift+f exec --no-startup-id firefox , mode "default"
+ bindsym t exec --no-startup-id kitty --single-instance, mode "default"
+ bindsym Shift+t exec --no-startup-id kitty --single-instance, mode "default"
+ bindsym e exec --no-startup-id pgrep emacs && i3-msg '[class="(?i)emacs"] focus' || emacs , mode "default"
+ bindsym Shift+e exec --no-startup-id emacs , mode "default"
+ bindsym p exec --no-startup-id pgrep pycharm && i3-msg '[class="(?i)pycharm"] focus' || pycharm , mode "default"
+ bindsym Shift+p exec --no-startup-id pycharm , mode "default"
+ bindsym j exec --no-startup-id pgrep idea && i3-msg '[class="(?i)idea"] focus' || idea , mode "default"
+ bindsym Shift+j exec --no-startup-id idea , mode "default"
+ bindsym d exec --no-startup-id pgrep discord && i3-msg '[class="(?i)discord"] focus' || discord , mode "default"
+ bindsym Shift+d exec --no-startup-id discord , mode "default"
+ bindsym s exec --no-startup-id pgrep slack && i3-msg '[class="(?i)slack"] focus' || slack , mode "default"
+ bindsym Shift+s exec --no-startup-id slack , mode "default"
+ bindsym z exec --no-startup-id pgrep zoom && i3-msg '[class="(?i)zoom"] focus' || zoom , mode "default"
+ bindsym Shift+z exec --no-startup-id zoom , mode "default"
+ bindsym w exec --no-startup-id pgrep virt-viewer && i3-msg '[class="(?i)virt-viewer"] focus' || virt-viewer --connect qemu:///system -w Windows10, mode "default"
+ bindsym Shift+w exec --no-startup-id virt-viewer --connect qemu:///system -w Windows10, mode "default"
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+ bindsym control+g mode "default"
+ bindsym control+bracketleft mode "default"
+}
+
+set $mode_space_goto [goto]: server
+mode --pango_markup "$mode_space_goto" {
+ bindsym s exec --no-startup-id rofi -show ssh, mode "default"
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+ bindsym control+g mode "default"
+ bindsym control+bracketleft mode "default"
+}
+
+set $mode_space_quit [quit]: [q]logout, reload, [Shift+r]restart, system
+mode --pango_markup "$mode_space_quit" {
+ bindsym q exec --no-startup-id i3-msg exit, mode "default"
+ bindsym r exec --no-startup-id i3_reconfigure, mode "default"
+ bindsym Shift+r restart, mode "default"
+ bindsym s mode "$mode_space_quit_system"
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+ bindsym control+g mode "default"
+ bindsym control+bracketleft mode "default"
+}
+
+set $mode_space_quit_system [quit][system]: suspend, reboot, power off
+mode --pango_markup "$mode_space_quit_system" {
+ bindsym s exec --no-startup-id systemctl suspend, mode "default"
+ bindsym r exec --no-startup-id systemctl reboot, mode "default"
+ bindsym p exec --no-startup-id systemctl poweroff, mode "default"
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+ bindsym control+g mode "default"
+ bindsym control+bracketleft mode "default"
+}
+
+bindsym $mod+space mode "$mode_space"
+
+set $ws_main 1: Main
+workspace "$ws_main" output DP-4
+
+set $ws_tasks 2: Tasks
+workspace "$ws_tasks" output DP-0
+assign [class="(?i)virt-viewer"] "$ws_tasks"
+
+set $ws_comms 3: Comms
+workspace "$ws_comms" output HDMI-0
+assign [class="(?i)discord"] "$ws_comms"
+assign [class="(?i)slack"] "$ws_comms"
+
+set $ws_python 4: Python
+workspace "$ws_python" output primary
+assign [class="(?i)pycharm"] "$ws_python"
+
+set $ws_java 5: Java
+workspace "$ws_java" output primary
+assign [class="(?i)idea"] "$ws_java"
+
+# xss-lock grabs a logind suspend inhibit lock and will use i3lock to lock the
+# screen before suspend. Use loginctl lock-session to lock your screen.
+exec --no-startup-id xss-lock --transfer-sleep-lock -- i3lock --nofork
+
+# Start i3bar to display a workspace bar (plus the system information i3status
+# finds out, if available)
+bar {
+ tray_output primary
+ tray_padding 20 px
+ status_command i3status
+}
+
+# Wallpaper
+exec --no-startup-id nitrogen --restore
+
diff --git a/i3/postscript b/i3/postscript
new file mode 100644
index 0000000..dbcc03c
--- /dev/null
+++ b/i3/postscript
@@ -0,0 +1,14 @@
+# xss-lock grabs a logind suspend inhibit lock and will use i3lock to lock the
+# screen before suspend. Use loginctl lock-session to lock your screen.
+exec --no-startup-id xss-lock --transfer-sleep-lock -- i3lock --nofork
+
+# Start i3bar to display a workspace bar (plus the system information i3status
+# finds out, if available)
+bar {
+ tray_output primary
+ tray_padding 20 px
+ status_command i3status
+}
+
+# Wallpaper
+exec --no-startup-id nitrogen --restore
\ No newline at end of file
diff --git a/i3/preamble b/i3/preamble
new file mode 100644
index 0000000..22834fe
--- /dev/null
+++ b/i3/preamble
@@ -0,0 +1,11 @@
+set $mod Mod4
+set $alt Mod1
+
+# Use Mouse+$mod to drag floating windows to their wanted position
+floating_modifier $alt
+
+# Font for window titles. Will also be used by the bar unless a different font
+# is used in the bar {} block below.
+font pango:DejaVu Sans Mono 10
+
+exec --no-startup-id compton