i3
Table of Contents
Main Config
Since I'm using i3
4.19 I don't have access to the new 'include' directive in 4.20. As a workaround for device specific settings I'm using yadm
's templating feature so I've included that template below instead of a working i3
config.
# DO NOT EDIT ~/.config/i3/config # MODIFY YOUR TEMPLATE ~/.config/i3/config##template # colors used, for reference #333333 dark grey #56ADBC teal blue #666666 charcoal grey #8C6BC8 deep lavendar #AE81FF liliac #E3E3DD light grey #FA9429 pumpkin #66D9EF robin's egg # Window Colors # class border backgr. text indicator child_border client.focused #333333 #56ADBC #333333 #FA9429 #56ADBC client.unfocused #333333 #333333 #E3E3DD #333333 #333333 client.focused_inactive #666666 #333333 #E3E3DD #333333 #333333 client.urgent #AE81FF #8C6BC8 #E3E3DD #8C6BC8 #8C6BC8 client.placeholder #333333 #333333 #E3E3DD #333333 #333333 client.background #E3E3DD # i3 config file (v4) # # Please see http://i3wm.org/docs/userguide.html for a complete reference! show_marks no set $mod Mod4 {% if yadm.hostname == "delta" %} font pango:Fira Code 11 {% else %} font pango:Fira Code 9 {% endif %} # start a terminal bindsym $mod+grave exec urxvt # rox-filer shortcuts bindsym $mod+Tab exec rox-filer # kill focused window bindsym $mod+Shift+apostrophe kill # start dmenu (a program launcher) bindsym $mod+p exec dmenu_run # There also is the (new) i3-dmenu-desktop which only displays applications # shipping a .desktop file. It is a wrapper around dmenu, so you need that # installed. bindsym $mod+y exec --no-startup-id i3-dmenu-desktop # change focus bindsym $mod+h focus left bindsym $mod+l focus right bindsym $mod+k focus up bindsym $mod+j focus down # move focused window bindsym $mod+Shift+h move left bindsym $mod+Shift+l move right bindsym $mod+Shift+k move up bindsym $mod+Shift+j move down # switch workspaces bindsym $mod+Left workspace prev bindsym $mod+Right workspace next # Rearrange workspaces bindsym $mod+Shift+Left move workspace to output left bindsym $mod+Shift+Down move workspace to output down bindsym $mod+Shift+Up move workspace to output up bindsym $mod+Shift+Right move workspace to output right # split in horizontal orientation bindsym $mod+s split h # split in vertical orientation bindsym $mod+v split v # enter fullscreen mode for the focused container bindsym $mod+u fullscreen toggle bindsym $mod+Shift+u fullscreen toggle global # change container layout (stacked, tabbed, toggle split) bindsym $mod+apostrophe layout stacking bindsym $mod+comma layout tabbed bindsym $mod+period layout toggle split # Use Mouse+$mod to drag floating windows to their wanted position floating_modifier $mod # toggle tiling / floating bindsym $mod+Shift+space floating toggle # change focus between tiling / floating windows bindsym $mod+space focus mode_toggle # always float geeqie for_window [class="Geeqie"] floating enable for_window [class="Gthumb"] floating enable #for_window [class="Remmina"] floating enable # scratchpad bindsym $mod+shift+z move scratchpad bindsym $mod+shift+v scratchpad show # focus the parent container bindsym $mod+a focus parent # focus the child container bindsym $mod+Shift+a focus child # switch to workspace bindsym $mod+1 workspace number 1 bindsym $mod+2 workspace number 2 bindsym $mod+3 workspace number 3 bindsym $mod+4 workspace number 4 bindsym $mod+5 workspace number 5 bindsym $mod+6 workspace number 6 bindsym $mod+7 workspace number 7 bindsym $mod+8 workspace number 8 bindsym $mod+9 workspace number 9 bindsym $mod+0 workspace number 10 bindsym $mod+F1 workspace number 11 bindsym $mod+F2 workspace number 12 bindsym $mod+F3 workspace number 13 bindsym $mod+F4 workspace number 14 bindsym $mod+F5 workspace number 15 bindsym $mod+F6 workspace number 16 bindsym $mod+F7 workspace number 17 bindsym $mod+F8 workspace number 18 bindsym $mod+F9 workspace number 19 bindsym $mod+F10 workspace number 20 # move focused container to workspace bindsym $mod+Shift+1 move container to workspace number 1 bindsym $mod+Shift+2 move container to workspace number 2 bindsym $mod+Shift+3 move container to workspace number 3 bindsym $mod+Shift+4 move container to workspace number 4 bindsym $mod+Shift+5 move container to workspace number 5 bindsym $mod+Shift+6 move container to workspace number 6 bindsym $mod+Shift+7 move container to workspace number 7 bindsym $mod+Shift+8 move container to workspace number 8 bindsym $mod+Shift+9 move container to workspace number 9 bindsym $mod+Shift+0 move container to workspace number 10 bindsym $mod+Shift+F1 move container to workspace number 11 bindsym $mod+Shift+F2 move container to workspace number 12 bindsym $mod+Shift+F3 move container to workspace number 13 bindsym $mod+Shift+F4 move container to workspace number 14 bindsym $mod+Shift+F5 move container to workspace number 15 bindsym $mod+Shift+F6 move container to workspace number 16 bindsym $mod+Shift+F7 move container to workspace number 17 bindsym $mod+Shift+F8 move container to workspace number 18 bindsym $mod+Shift+F9 move container to workspace number 19 bindsym $mod+Shift+F10 move container to workspace number 20 # new workspace bindsym $mod+n exec "i3-msg workspace number $(($(i3-msg -t get_workspaces |jq -r '.[].name'|cut -d':' -f 1|sort -n| tail -1) + 1))" # work with workspaces bindsym $mod+Shift+r exec i3-msg rename workspace \ $(i3-msg -t get_workspaces | jq '.[] | select(.focused==true).name') \ to \ $(i3-msg -t get_workspaces | jq '.[] | select(.focused==true).num'):$(zenity --entry --title 'Rename workspace' --text "New workspace name:") bindsym $mod+Mod1+Shift+m exec "~/.config/i3/ws_main.sh" bindsym $mod+Mod1+Shift+c exec "~/.config/i3/ws_new_code.sh" bindsym $mod+Mod1+Shift+d exec "~/.config/i3/ws_new_doc.sh" # remark windows that are already swallowed for_window [class="^Atril$|^Devhelp$|^Evince$|^Gvim$|^calibre$" workspace="^\d+:doc" con_mark="^swallowed\d$"] mark --replace unswallowed # toggle it. (swallowed windows become unmarked, unswallowed windows are now marked) for_window [class="^Atril$|^Devhelp$|^Evince$|^Gvim$|^calibre$" workspace="^\d+:doc"] mark --toggle unswallowed # move unswallowed windows to the desired mark for_window [class="^Atril$|^Devhelp$|^Evince$|^Gvim$|^calibre$" workspace="^\d+:doc" con_mark="^unswallowed$"] move window to mark doc # Lock with xscreensaver (Hyper+Alt+l) {% if yadm.hostname == "dreadnought" %} # Save screenshots of all workspaces, kill steam because it breaks DPMS, then lock bindsym $mod+Mod1+l exec "~/.scripts/i3_screenshot.py; killall steam; xscreensaver-command -lock" {% else %} bindsym $mod+Mod1+l exec "xscreensaver-command -lock" {% endif %} # reload the configuration file bindsym $mod+Shift+y reload # restart i3 inplace (preserves your layout/session, can be used to upgrade i3) bindsym $mod+Shift+p restart # exit i3 (logs you out of your X session) bindsym $mod+Shift+period exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -b 'Yes, exit i3' 'i3-msg exit'" # mouse actions bindsym --release $mod+button2 kill # Issue #2523 numlock is somehow Mod2. dumb. bindsym --release Mod2+button10 kill bindsym --release button10 kill bindsym --release $mod+button3 floating toggle # move all but main (1) workspace to chosen output # (uses xrandr, grep, cut, zenity, i3-msg, jq) bindsym $Mod+Shift+m exec "~/.config/i3/mv_ws.sh" # change screen layout mode "screenlayout" { bindsym grave exec autorandr common; mode "default" bindsym 1 exec autorandr --load "Magedok 9 - |⠀⡀⠀⠀|"; mode "default" bindsym 2 exec autorandr --load "U2414H - |⠰⠶⠀⠀|"; mode "default" bindsym 3 exec autorandr --load "U2515H - |⠀⠀⠿⠿|"; mode "default" bindsym 4 exec autorandr --load "MageDok 15 - |⠈⠁⠀⠀|"; mode "default" bindsym 5 exec autorandr --load "All - |⠸⡷⠿⠿|"; mode "default" # new background bindsym 0 exec --no-startup-id ~/.fehbg # reset background if it gets shifted bindsym 9 exec --no-startup-id feh --no-fehbg --bg-fill "$(cat ~/.last_fehbg)" bindsym t exec ~/.screenlayout/touch_display.sh bindsym $mod+x mode "default" bindsym Return mode "default" bindsym Escape mode "default" } bindsym $mod+x mode "screenlayout" bindsym $mod+Shift+x exec "~/.screenlayout/autorandr.sh" #Toggle touchscreen/touchpad bindsym $mod+Shift+s exec --no-startup-id notify-send "$(~/.config/i3/toggle_ts.sh)" -t 1400 bindsym $mod+Shift+t exec --no-startup-id notify-send "$(~/.config/i3/toggle_tp.sh)" -t 1400 # Multimedia, brightness keys {% if yadm.hostname == "dreadnought" %} bindsym XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume alsa_output.usb-0d8c_USB_Sound_Device-00.analog-stereo +3% && pactl set-sink-volume alsa_output.pci-0000_0f_00.6.analog-stereo +3% bindsym XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume alsa_output.usb-0d8c_USB_Sound_Device-00.analog-stereo -3% && pactl set-sink-volume alsa_output.pci-0000_0f_00.6.analog-stereo -3% bindsym XF86AudioMute exec --no-startup-id pactl set-sink-mute alsa_output.usb-0d8c_USB_Sound_Device-00.analog-stereo toggle && pactl set-sink-mute alsa_output.pci-0000_0f_00.6.analog-stereo toggle {% else %} bindsym XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume 0 +3% bindsym XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume 0 -3% bindsym XF86AudioMute exec --no-startup-id pactl set-sink-mute 0 toggle {% endif %} bindsym XF86Display exec --no-startup-id lxrandr bindsym XF86MonBrightnessUp exec brightnessctl s +10% bindsym XF86MonBrightnessDown exec brightnessctl s 10%- bindsym XF86AudioPlay exec playerctl play-pause bindsym XF86AudioPrev exec playerctl previous bindsym XF86AudioNext exec playerctl next bindsym $mod+Shift+Mod1+space exec playerctl play-pause bindsym $mod+Up exec playerctl previous bindsym $mod+Down exec playerctl next # Start i3bar to display a workspace bar (plus the system information i3status # finds out, if available) bar { status_command i3status tray_output primary bindsym button2 exec "i3-msg workspace number $(($(i3-msg -t get_workspaces |jq -r '.[].name'|cut -d':' -f 1|sort -n| tail -1) + 1))" bindsym button4 workspace prev_on_output bindsym button5 workspace next_on_output colors { background #1B1D1E statusline #E3E3DD focused_workspace #666666 #66D9EF #333333 active_workspace #666666 #333333 #56ADBC urgent_workspace #666666 #FA9429 #333333 binding_mode #666666 #FA9429 #333333 } } # cursor setup, background exec --no-startup-id xsetroot -cursor_name left_ptr exec --no-startup-id ~/.fehbg # autostart apps exec --no-startup-id picom exec --no-startup-id /usr/lib/x86_64-linux-gnu/xfce4/notifyd/xfce4-notifyd exec --no-startup-id keepassx exec --no-startup-id remmina -i exec --no-startup-id krb5-auth-dialog exec --no-startup-id pasystray exec --no-startup-id nm-applet exec --no-startup-id udiskie --tray exec --no-startup-id xfce4-notes exec --no-startup-id kdeconnect-indicator exec --no-startup-id blueman-applet exec --no-startup-id usbguard-applet-qt exec --no-startup-id fusuma --daemon {% if yadm.hostname == "dreadnought" %} exec --no-startup-id ~/.dropbox/dropbox_start.py {% endif %} {% if yadm.hostname == "delta" %} exec --no-startup-id ~/.dropbox/dropbox_start.py exec --no-startup-id brightnessctl --device='tpacpi::kbd_backlight' set 1 {% else %} exec --no-startup-id xscreensaver -nosplash {% endif %} {% if yadm.hostname == "delta" %} # remarkable vnsee display # exec --no-startup-id xrandr --newmode 1404x1872 0 1404 1404 1404 1404 1872 1872 1872 1872 # exec --no-startup-id xrandr --addmode VIRTUAL1 1404x1872 # exec --no-startup-id xrandr --output VIRTUAL1 --mode 1404x1872 --right-of eDP1 # bindsym $mod+F11 exec "pkill -f 'x11vnc -repeat -forever -nocursor -allow 10.11.99.1'" # bindsym $mod+F12 exec "x11vnc -repeat -forever -nocursor -allow 10.11.99.1 -nopw -clip $(xrandr | perl -n -e'/VIRTUAL1 .*?(\d+x\d+\+\d+\+\d+)/ && print $1')" {% endif %} ############################################## ############ Русский layout binds ############ ############################################## bindsym $mod+less exec urxvt # kill focused window bindsym $mod+Shift+Cyrillic_SHORTI kill # start dmenu, i3-dmenu-desktop bindsym $mod+Cyrillic_ka exec dmenu_run bindsym $mod+Cyrillic_ie exec --no-startup-id i3-dmenu-desktop # change focus bindsym $mod+Cyrillic_o focus left bindsym $mod+Cyrillic_ze focus right bindsym $mod+Cyrillic_em focus up bindsym $mod+Cyrillic_es focus down # move focused window bindsym $mod+Shift+Cyrillic_O move left bindsym $mod+Shift+Cyrillic_ZE move right bindsym $mod+Shift+Cyrillic_EM move up bindsym $mod+Shift+Cyrillic_ES move down # split in horizontal orientation bindsym $mod+Cyrillic_zhe split h # split in vertical orientation bindsym $mod+Cyrillic_yu split v # enter fullscreen mode for the focused container bindsym $mod+Cyrillic_a fullscreen toggle bindsym $mod+Shift+Cyrillic_A fullscreen toggle global # change container layout (stacked, tabbed, toggle split) bindsym $mod+Cyrillic_shorti layout stacking bindsym $mod+Cyrillic_tse layout tabbed bindsym $mod+Cyrillic_u layout toggle split # scratchpad binds omitted, unused, and I don't want to bind the equivalent keys # focus the parent container bindsym $mod+Cyrillic_ef focus parent # focus the child container bindsym $mod+Shift+Cyrillic_EF focus child # new workspace bindsym $mod+Cyrillic_de exec "i3-msg workspace number $(($(i3-msg -t get_workspaces |jq -r '.[].name'|cut -d':' -f 1|sort -n| tail -1) + 1))" # work with workspaces bindsym $mod+Shift+Cyrillic_SHCHA exec i3-msg rename workspace \ $(i3-msg -t get_workspaces | jq '.[] | select(.focused==true).name') \ to \ $(i3-msg -t get_workspaces | jq '.[] | select(.focused==true).num'):$(zenity --entry --title 'Rename workspace' --text "New workspace name:") bindsym $mod+Mod1+Shift+Cyrillic_SOFTSIGN exec "~/.config/i3/ws_main.sh" bindsym $mod+Mod1+Shift+Cyrillic_SHA exec "~/.config/i3/ws_new_code.sh" bindsym $mod+Mod1+Shift+Cyrillic_ER exec "~/.config/i3/ws_new_doc.sh" # Lock with xscreensaver (Hyper+Alt+l) bindsym $mod+Mod1+Cyrillic_ze exec "xscreensaver-command -lock" # reload the configuration file bindsym $mod+Shift+Cyrillic_IE reload # restart i3 inplace (preserves your layout/session, can be used to upgrade i3) bindsym $mod+Shift+Cyrillic_KA restart # exit i3 (logs you out of your X session) bindsym $mod+Shift+Cyrillic_U exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -b 'Yes, exit i3' 'i3-msg exit'" # move all but main (1) workspace to chosen output # (uses xrandr, grep, cut, zenity, i3-msg, jq) bindsym $Mod+Shift+Cyrillic_SOFTSIGN exec "~/.config/i3/mv_ws.sh" # change screen layout mode "screenlayout" { bindsym $mod+Cyrillic_i mode "default" } bindsym $mod+Cyrillic_i mode "screenlayout" #Toggle touchscreen/touchpad bindsym $mod+Shift+Cyrillic_ZHE exec --no-startup-id notify-send "$(~/.config/i3/toggle_ts.sh)" -t 1400 bindsym $mod+Shift+Cyrillic_EL exec --no-startup-id notify-send "$(~/.config/i3/toggle_tp.sh)" -t 1400
Helper utilites
i3bar
Stock i3 status and workspace bar
i3status
My default config:
# i3status configuration file. # see "man i3status" for documentation. # It is important that this file is edited as UTF-8. # The following line should contain a sharp s: # ß # If the above line is not correctly displayed, fix your editor first! general { colors = true color_good = "#cfd0c2" color_bad = "#fc981f" color_degraded = "#e7db75" interval = 5 } order += "disk /" order += "disk /home" order += "wireless _first_" order += "ethernet _first_" order += "battery all" order += "load" order += "volume desktop" order += "tztime local" wireless _first_ { format_up = "W: (%quality at %essid) %ip" format_down = "" } ethernet _first_ { # if you use %speed, i3status requires root privileges format_up = "E: %ip (%speed)" format_down = "" color_degraded = "#cfd0c2" } battery all { format = "%status %percentage %remaining" format_down = "" status_chr = "⚡" status_unk = "⚡" status_bat = "🔋" low_threshold = 10 threshold_type = time integer_battery_capacity = true } tztime local { # abbr. weekday YYYY-MM-DD HH:mm:ss format = "%a %F %T" } load { format = "%1min" } disk "/" { format = "root: %avail" } disk "/home" { format = "home: %avail" } volume desktop { format = "🔊: %volume" format_muted = "🔊: muted (%volume)" device = "pulse:0" }
My main desktop config, notable for the volume blocks being manually specified:
# i3status configuration file. # see "man i3status" for documentation. # It is important that this file is edited as UTF-8. # The following line should contain a sharp s: # ß # If the above line is not correctly displayed, fix your editor first! general { colors = true color_good = "#cfd0c2" color_bad = "#fc981f" color_degraded = "#e7db75" interval = 5 } order += "disk /" order += "disk /home" order += "wireless _first_" order += "ethernet _first_" order += "battery all" order += "load" order += "volume desktop" order += "volume music" order += "tztime local" wireless _first_ { format_up = "W: (%quality at %essid) %ip" format_down = "" } ethernet _first_ { # if you use %speed, i3status requires root privileges format_up = "E: %ip (%speed)" format_down = "" color_degraded = "#cfd0c2" } battery all { format = "%status %percentage Rem:%remaining" format_down = "" status_chr = "⚡" status_bat = "🔋" low_threshold = 10 threshold_type = time integer_battery_capacity = true } tztime local { # abbr. weekday YYYY-MM-DD HH:mm:ss format = "%a %F %T" } load { format = "%1min" } disk "/" { format = "root: %avail" } disk "/home" { format = "home: %avail" } volume desktop { format = "🔊: %volume" format_muted = "🔊: muted (%volume)" device = "pulse:alsa_output.pci-0000_0f_00.6.analog-stereo" } volume music { format = "♪: %volume" format_muted = "♪: muted (%volume)" device = "pulse:alsa_output.usb-0d8c_USB_Sound_Device-00.analog-stereo" }
feh
For setting backgrounds. I have this small script to randomly select a file and track the previous wallpaper for reverting.
#!/bin/sh wallpaper_path="$(find ~/.wallpapers/|sort -R|tail -n 1)" echo "$wallpaper_path" > ~/.last_fehbg feh --no-fehbg --bg-fill "$wallpaper_path"
Scripts
Load a predefined workspace/layout
Main workspace
#!/bin/bash workspace_shape=$(i3-msg -t get_workspaces|jq -r '.[]|select (.focused==true)|if .rect.width >= .rect.height then "landscape" else "portrait" end') # Select currently focused workspace number workspace_number=$(($(i3-msg -t get_workspaces|jq -r '.[]|select (.focused==true)|.name' \ |cut -d':' -f 1 |sort -n| tail -1))) # Rename workspace i3-msg rename workspace to "$workspace_number:main" # Load appropriate layout if [ "$workspace_shape" == "portrait" ]; then i3-msg append_layout "$HOME/.config/i3/main-portrait.json" elif [ "$workspace_shape" == "landscape" ]; then i3-msg append_layout "$HOME/.config/i3/main-landscape.json" fi i3-msg "exec cantata; exec claws-mail; exec firefox; exec hexchat"
Main layouts
// vim:ts=4:sw=4:et { // tabbed split container with 3 children "border": "normal", "floating": "auto_off", "layout": "tabbed", "percent": 0.4, "type": "con", "nodes": [ { "border": "normal", "current_border_width": 2, "floating": "auto_off", "percent": 0.333333333333333, "swallows": [ { "class": "^Hexchat$", "instance": "^hexchat$" } ], "type": "con" }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "percent": 0.333333333333333, "swallows": [ { "class": "^Claws\\-mail$", "instance": "^claws\\-mail$" } ], "type": "con" }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "percent": 0.333333333333333, "swallows": [ { "class": "^cantata$", "instance": "^cantata$" } ], "type": "con" } ] } { // tabbed split container with 2 children "border": "normal", "floating": "auto_off", "layout": "tabbed", "percent": 0.6, "type": "con", "nodes": [ { "border": "normal", "current_border_width": 2, "floating": "auto_off", "percent": 0.5, "swallows": [ { "class": "^Firefox\\-esr$", "instance": "^Navigator$" } ], "type": "con" }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "geometry": { "height": 1400, "width": 1772, "x": 1920, "y": 352 }, "name": "KeePassX", "percent": 0.5, "swallows": [ { "class": "^Keepassx$", "instance": "^keepassx$" } ], "type": "con" } ] }
// vim:ts=4:sw=4:et { // splitv split container with 2 children "border": "normal", "floating": "auto_off", "layout": "splitv", "percent": 0.365, "type": "con", "nodes": [ { // tabbed split container with 2 children "border": "normal", "floating": "auto_off", "layout": "tabbed", "percent": 0.5, "type": "con", "nodes": [ { "border": "normal", "current_border_width": 2, "floating": "auto_off", "percent": 0.333333333333333, "swallows": [ { "class": "^Claws\\-mail$", "instance": "^claws\\-mail$" } ], "type": "con" }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "percent": 0.333333333333333, "swallows": [ { "class": "^cantata$", "instance": "^cantata$" } ], "type": "con" } ] }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "percent": 0.5, "swallows": [ { "class": "^Hexchat$", "instance": "^hexchat$" } ], "type": "con" } ] } { // tabbed split container with 2 children "border": "normal", "floating": "auto_off", "layout": "tabbed", "percent": 0.635, "type": "con", "nodes": [ { "border": "normal", "current_border_width": 2, "floating": "auto_off", "percent": 0.5, "swallows": [ { "class": "^Keepassx$", "instance": "^keepassx$" } ], "type": "con" }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "percent": 0.5, "swallows": [ { "class": "^Firefox\\-esr$", "instance": "^Navigator$" } ], "type": "con" } ] }
Code workspace
#!/bin/bash workspace_shape=$(i3-msg -t get_workspaces|jq -r '.[]|select (.focused==true)|if .rect.width >= .rect.height then "landscape" else "portrait" end') if [ "$workspace_shape" == "portrait" ]; then workspace_size=$(i3-msg -t get_workspaces|jq -r '.[]|select (.focused==true)|.rect.height') elif [ "$workspace_shape" == "landscape" ]; then workspace_size=$(i3-msg -t get_workspaces|jq -r '.[]|select (.focused==true)|.rect.width') fi # Select next highest unused number workspace_number=$(($(i3-msg -t get_workspaces |jq -r '.[].name' \ |cut -d':' -f 1 |sort -n| tail -1) + 1)) # Switch to and rename workspace i3-msg workspace number $workspace_number if [ -d "$1" ]; then i3-msg rename workspace to "$workspace_number:$(basename "$1")" else i3-msg rename workspace to "$workspace_number:code" fi # Load appropriate layout if [ "$workspace_shape" == "portrait" ]; then # TODO make a portrait layout when the need arises # We still spawn a workplace for now because it's easy to select # all windows and move them to the correct monitor if [ "$workspace_size" -lt 2000 ]; then i3-msg append_layout "$HOME/.config/i3/code_1080_landscape.json" else i3-msg append_layout "$HOME/.config/i3/code_2560_landscape.json" fi elif [ "$workspace_shape" == "landscape" ]; then if [ "$workspace_size" -lt 2000 ]; then i3-msg append_layout "$HOME/.config/i3/code_1080_landscape.json" else i3-msg append_layout "$HOME/.config/i3/code_2560_landscape.json" fi fi # Run applications to fill layout if [ -d "$1" ]; then i3-msg "exec rox-filer '$1'; exec urxvt -cd '$1'; exec urxvt -cd '$1'; exec gvim '$1'; exec gvim '$1'" if [ "$workspace_size" -gt 2000 ]; then # extra filer/editor for larger screens i3-msg "exec rox-filer '$1'; exec gvim '$1'" fi else i3-msg "exec rox-filer; exec urxvt; exec urxvt; exec gvim ./; exec gvim ./" if [ "$workspace_size" -gt 2000 ]; then # extra filer/editor for larger screens i3-msg "exec rox-filer; exec gvim ./" fi fi
Code layouts
// vim:ts=4:sw=4:et // Code layout for 1920x1080p landscape displays { // splith split container with 3 children "border": "normal", "floating": "auto_off", "layout": "splith", "percent": 1, "type": "con", "nodes": [ { // splitv split container with 3 children "border": "normal", "floating": "auto_off", "layout": "splitv", "percent": 0.25, "type": "con", "nodes": [ { "border": "normal", "current_border_width": 2, "floating": "auto_off", "geometry": { "height": 282, "width": 400, "x": 0, "y": 0 }, "percent": 0.25, "swallows": [ { "class": "^Rox\\-filer$", "instance": "^rox\\-filer$" } ], "type": "con" }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "geometry": { "height": 316, "width": 400, "x": 0, "y": 0 }, "percent": 0.25, "swallows": [ { "class": "^URxvt$", "instance": "^urxvt$" } ], "type": "con" }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "geometry": { "height": 316, "width": 400, "x": 0, "y": 0 }, "percent": 0.25, "swallows": [ { "class": "^URxvt$", "instance": "^urxvt$" } ], "type": "con" } ] }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "geometry": { "height": 316, "width": 754, "x": 0, "y": 0 }, "percent": 0.375, "swallows": [ { "class": "^Gvim$", "instance": "^gvim$" } ], "type": "con" }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "geometry": { "height": 316, "width": 754, "x": 0, "y": 0 }, "percent": 0.375, "swallows": [ { "class": "^Gvim$", "instance": "^gvim$" } ], "type": "con" } ] }
// vim:ts=4:sw=4:et // Code layout for 2560x1440 landscape displays { // splith split container with 4 children "border": "normal", "floating": "auto_off", "layout": "splith", "percent": 1, "type": "con", "nodes": [ { // splitv split container with 4 children "border": "normal", "floating": "auto_off", "layout": "splitv", "percent": 0.25, "type": "con", "nodes": [ { "border": "normal", "current_border_width": 2, "floating": "auto_off", "geometry": { "height": 282, "width": 564, "x": 0, "y": 0 }, "percent": 0.25, "swallows": [ { "class": "^Rox\\-filer$", "instance": "^rox\\-filer$" } ], "type": "con" }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "geometry": { "height": 146, "width": 304, "x": 0, "y": 0 }, "percent": 0.25, "swallows": [ { "class": "^Rox\\-filer$", "instance": "^rox\\-filer$" } ], "type": "con" }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "geometry": { "height": 316, "width": 576, "x": 0, "y": 0 }, "percent": 0.25, "swallows": [ { "class": "^URxvt$", "instance": "^urxvt$" } ], "type": "con" }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "geometry": { "height": 316, "width": 576, "x": 0, "y": 0 }, "percent": 0.25, "swallows": [ { "class": "^URxvt$", "instance": "^urxvt$" } ], "type": "con" } ] }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "geometry": { "height": 316, "width": 576, "x": 0, "y": 0 }, "percent": 0.25, "swallows": [ { "class": "^Gvim$", "instance": "^gvim$" } ], "type": "con" }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "geometry": { "height": 316, "width": 576, "x": 0, "y": 0 }, "percent": 0.25, "swallows": [ { "class": "^Gvim$", "instance": "^gvim$" } ], "type": "con" }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "geometry": { "height": 316, "width": 576, "x": 0, "y": 0 }, "percent": 0.25, "swallows": [ { "class": "^Gvim$", "instance": "^gvim$" } ], "type": "con" } ] }
Documentation workspace
#!/bin/bash workspace_shape=$(i3-msg -t get_workspaces|jq -r '.[]|select (.focused==true)|if .rect.width >= .rect.height then "landscape" else "portrait" end') # Select next highest unused number workspace_number=$(($(i3-msg -t get_workspaces |jq -r '.[].name' \ |cut -d':' -f 1 |sort -n| tail -1) + 1)) # Switch to and rename workspace i3-msg workspace number $workspace_number i3-msg rename workspace to "$workspace_number:doc" # Load appropriate layout if [ "$workspace_shape" == "portrait" ]; then i3-msg append_layout "$HOME/.config/i3/doc-portrait.json" elif [ "$workspace_shape" == "landscape" ]; then i3-msg append_layout "$HOME/.config/i3/doc-landscape.json" fi # Run applications to fill layout i3-msg exec rox-filer "$HOME/documents"
Documentation layouts
// vim:ts=4:sw=4:et { // splith split container with 1 children "border": "normal", "floating": "auto_off", "layout": "splith", "percent": 0.294520547945205, "type": "con", "nodes": [ { "border": "normal", "current_border_width": 2, "floating": "auto_off", "geometry": { "height": 304, "width": 657, "x": 0, "y": 0 }, "percent": 1, "swallows": [ { "class": "^Rox\\-filer$", "instance": "^rox\\-filer$" } ], "type": "con" } ] } { // tabbed split container "border": "normal", "floating": "auto_off", "layout": "tabbed", "percent": 0.705479452054795, "type": "con", "swallows": [ { "class": "^Atril$|^Devhelp$|^Evince$|^Gvim$|^calibre$", "instance": "^atril$|^calibre\\-ebook\\-viewer$|^devhelp$|^evince$|^gvim$" } ], "nodes": [ { "border": "normal", "current_border_width": 2, "floating": "auto_off", "percent": 0.5, "swallows": [ { "class": "^Atril$|^Devhelp$|^Evince$|^Gvim$|^calibre$", "instance": "^atril$|^calibre\\-ebook\\-viewer$|^devhelp$|^evince$|^gvim$" } ], "type": "con", "marks": [ "swallowed1" ] }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "percent": 0.5, "swallows": [ { "class": "^Atril$|^Devhelp$|^Evince$|^Gvim$|^calibre$", "instance": "^atril$|^calibre\\-ebook\\-viewer$|^devhelp$|^evince$|^gvim$" } ], "type": "con", "marks": [ "swallowed1" ] } ], "mark": "doc" }
// vim:ts=4:sw=4:et { "border": "normal", "current_border_width": 2, "floating": "auto_off", "geometry": { "height": 480, "width": 657, "x": 0, "y": 0 }, "percent": 0.22, "swallows": [ { "class": "^Rox\\-filer$", "instance": "^rox\\-filer$" } ], "type": "con" } { // tabbed split container with 2 children "border": "normal", "floating": "auto_off", "layout": "tabbed", "percent": 0.39, "type": "con", "nodes": [ { "border": "normal", "current_border_width": 2, "floating": "auto_off", "percent": 0.5, "swallows": [ { "class": "^Atril$|^Devhelp$|^Evince$|^Gvim$|^calibre$", "instance": "^atril$|^calibre\\-ebook\\-viewer$|^devhelp$|^evince$|^gvim$" } ], "type": "con", "marks": [ "swallowed1" ] }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "percent": 0.5, "swallows": [ { "class": "^Atril$|^Devhelp$|^Evince$|^Gvim$|^calibre$", "instance": "^atril$|^calibre\\-ebook\\-viewer$|^devhelp$|^evince$|^gvim$" } ], "type": "con", "marks": [ "swallowed2" ] } ], "mark": "doc2" } { // tabbed split container with 2 children "border": "normal", "floating": "auto_off", "layout": "tabbed", "percent": 0.39, "type": "con", "nodes": [ { "border": "normal", "current_border_width": 2, "floating": "auto_off", "percent": 0.5, "swallows": [ { "class": "^Atril$|^Devhelp$|^Evince$|^Gvim$|^calibre$", "instance": "^atril$|^calibre\\-ebook\\-viewer$|^devhelp$|^evince$|^gvim$" } ], "type": "con", "marks": [ "swallowed3" ] }, { "border": "normal", "current_border_width": 2, "floating": "auto_off", "percent": 0.5, "swallows": [ { "class": "^Atril$|^Devhelp$|^Evince$|^Gvim$|^calibre$", "instance": "^atril$|^calibre\\-ebook\\-viewer$|^devhelp$|^evince$|^gvim$" } ], "type": "con", "marks": [ "swallowed4" ] } ], "mark": "doc" }
Screen layout (autorandr)
Screen layout relies on autorandr
and a script to select from detected layouts.
The script uses zenity to display simple dialog with all detected inputs. This is because complexity exploded with 4 displays, and it was no longer feasible to remember or fit all the screen configurations on a reasonable set of binds.
#!/bin/bash autorandr --match-edid --force --load "$(autorandr --match-edid --detected | sed 's/ - /\n/'| zenity --list --print-column=ALL --title "Which layout?" --column "Description" --column "Layout" --separator=" - " --height 300 --width 200)"
The autorandr postswitch scripts still rely on my older support scripts for some things. These get output names from monitor serial numbers since outputs naming isn't idempotent.
#!/bin/sh # for i in /sys/class/graphics/fb0/device/drm/card0/*/edid; do edid-decode < $i; done|grep Product u2515h_serial="123456789" u2515h=$(~/.screenlayout/serial2output.sh "$u2515h_serial") export u2515h u2414h_one_serial="123456789" u2414h_one=$(~/.screenlayout/serial2output.sh "$u2414h_one_serial") export u2414h_one magedok_15_serial="123456789" magedok_15=$(~/.screenlayout/serial2output.sh "$magedok_15_serial") export magedok_15 magedok_9_serial="123456789" magedok_9=$(~/.screenlayout/serial2output.sh "$magedok_9_serial") export magedok_9 # not currently used displays u2414h_two_serial="123456789" u2414h_two=$(~/.screenlayout/serial2output.sh "$u2414h_two_serial") export u2414h_two # one of two 11.6 1080p, probably the one with the vesa mount screwholes magedok_vesa_serial="123456789" magedok_vesa=$(~/.screenlayout/serial2output.sh "$magedok_vesa_serial") export magedok_vesa
#!/bin/bash # this probably came from: https://stackoverflow.com/a/24933353 while read -r output hex; do serial=$(echo "$hex" | xxd -r -p -) if [ "$1" = "$serial" ]; then echo "$output" exit fi done < <(xrandr --prop | awk ' !/^[ \t]/ { if (output && hex) print output, hex output=$1 hex="" } /[:.]/ && h { sub(/.*000000ff00/, "", hex) hex = substr(hex, 0, 26) "0a" sub(/0a.*/, "", hex) h=0 } h {sub(/[ \t]+/, ""); hex = hex $0} /EDID.*:/ {h=1} END {if (output && hex) print output, hex} ' | sort )
autorandr postswitch scripts
#!/bin/sh sleep 2 i3-msg restart feh --no-fehbg --bg-fill "$(cat ~/.last_fehbg)"
#!/bin/sh sleep 6 . ~/.screenlayout/screen_names.sh if xrandr --listactivemonitors | grep -q "$magedok_9"; then xinput enable "ILITEK ILITEK-TP" xinput map-to-output "ILITEK ILITEK-TP" "$magedok_9" else xinput disable "ILITEK ILITEK-TP" fi if xrandr --listactivemonitors | grep -q "$magedok_15"; then xinput enable "G2Touch Multi-Touch by G2TSP" xinput map-to-output "G2Touch Multi-Touch by G2TSP" "$magedok_15" else xinput disable "G2Touch Multi-Touch by G2TSP" fi if xrandr --listactivemonitors | grep -q "$magedok_vesa"; then xinput enable "N-trig DuoSense" xinput enable "N-trig DuoSense Mouse" xinput enable "N-trig DuoSense Stylus" xinput map-to-output "N-trig DuoSense" "$magedok_vesa" xinput map-to-output "N-trig DuoSense Mouse" "$magedok_vesa" xinput map-to-output "N-trig DuoSense Stylus" "$magedok_vesa" else xinput disable "N-trig DuoSense" xinput disable "N-trig DuoSense Mouse" xinput disable "N-trig DuoSense Stylus" fi
Move workspaces script
Script to move all workspaces to a particular display (excluding workspace 1). This is because I find taking away displays from i3 isn't always predictable. If I pre-move workspaces then I can disable monitors without annoying hiccups.
This script prompts the user for the target output using zenity
.
#!/bin/bash if output=$(xrandr --current --query|grep " connected "|cut -d ' ' -f 1 | zenity --list --title "Which output?" --column "Outputs"); then while read -r name; do i3-msg workspace "${name}" i3-msg move workspace to output "$output" done< <(i3-msg -t get_workspaces | jq -r '.[]|select (.num>1) | "\(.name)"') fi
Disable input device scripts
These are for the Thinkpad X280. Just simple toggles for the touchpad and touchscreen when I don't want them.
#!/bin/bash declare -i ID ID=`xinput list | grep -Eo 'Synaptics TM3381-002\s*id\=[0-9]{1,2}' | grep -Eo 'id\=[0-9]{1,2}'|grep -Eo '[0-9]{1,2}'` declare -i STATE STATE=`xinput list-props $ID|grep 'Device Enabled'|awk '{print $4}'` if [ $STATE -eq 1 ] then xinput disable $ID echo "Touchpad disabled." else xinput enable $ID echo "Touchpad enabled." fi
#!/bin/bash declare -i ID ID=`xinput list | grep -Eo 'Touchscreen\s*id\=[0-9]{1,2}' | grep -Eo 'id\=[0-9]{1,2}'|grep -Eo '[0-9]{1,2}'` declare -i STATE STATE=`xinput list-props $ID|grep 'Device Enabled'|awk '{print $4}'` if [ $STATE -eq 1 ] then xinput disable $ID echo "Touchscreen disabled." else xinput enable $ID echo "Touchscreen enabled." fi