From ab95163c5b5012c8e0b1ad3494ee315ebbd700a8 Mon Sep 17 00:00:00 2001 From: TheEdgeOfRage Date: Sun, 4 Feb 2024 15:38:14 +0100 Subject: [PATCH] Add sway config --- dot_config/bumblebee-status/top.toml.tmpl | 4 +- dot_config/sway/autoname-workspaces.py | 238 +++++++++++++++++++ dot_config/sway/config.tmpl | 264 ++++++++++++++++++++++ 3 files changed, 504 insertions(+), 2 deletions(-) create mode 100644 dot_config/sway/autoname-workspaces.py create mode 100644 dot_config/sway/config.tmpl diff --git a/dot_config/bumblebee-status/top.toml.tmpl b/dot_config/bumblebee-status/top.toml.tmpl index ca2af01..739bbcc 100644 --- a/dot_config/bumblebee-status/top.toml.tmpl +++ b/dot_config/bumblebee-status/top.toml.tmpl @@ -1,5 +1,5 @@ [core] -modules=playerctl, dunst, {{ .i3.bumblebee.modules }} sensors, pulsein, pulseout, date, time +modules=playerctl, dunst, {{ .bumblebee.modules }} sensors, pulsein, pulseout, date, time autohide=#comma-separated list of modules to hide, unless in warning/error state theme=gruvbox-powerline @@ -24,5 +24,5 @@ date.format=%a, %Y-%m-%d date.interval=30 time.format=%H:%M:%S time.interval=1 -{{ range $_, $param := .i3.bumblebee.parameters -}}{{ $param.key }}={{ $param.value }} +{{ range $_, $param := .bumblebee.parameters -}}{{ $param.key }}={{ $param.value }} {{end}} diff --git a/dot_config/sway/autoname-workspaces.py b/dot_config/sway/autoname-workspaces.py new file mode 100644 index 0000000..e1522b6 --- /dev/null +++ b/dot_config/sway/autoname-workspaces.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python3 + +# This script listens for i3 events and updates workspace names to show icons +# for running programs. It contains icons for a few programs, but more can +# easily be added by inserting them into WINDOW_ICONS below. +# +# Dependencies: xorg-xprop i3ipc fontawesome +# +# Installation: +# Add "exec_always ~/.config/i3/i3-autoname-workspaces.py &" to the i3 config +# +# Configuration: +# The default i3 config's keybingings reference workspaces by name, which is an +# issue when using this script because the "names" are constantaly changing to +# show window icons. Instead, you'll need to change the keybindings to +# reference workspaces by number. Change lines like: +# bindsym $mod+1 workspace 1 +# To: +# bindsym $mod+1 workspace number 1 + +import re +import signal +import subprocess as proc +from types import FrameType +from typing import Any, Callable + +import nerdfonts as nf +from i3ipc import Connection + +# Add icons here for common programs you use. The keys are the X window class +# (WM_CLASS) names (lower-cased) and the icons can be any text you want to +# display. +# +# These icons are from the Nerd Font patch set. +# You can find a list of all here: https://www.nerdfonts.com/cheat-sheet +# +# If you're not sure what the WM_CLASS is for your application, you can use +# xprop (https://linux.die.net/man/1/xprop). Run `xprop | grep WM_CLASS` +# then click on the application you want to inspect. +WINDOW_ICONS: dict[str, str] = { + 'alacritty': nf.icons['fa_terminal'], + 'blender': nf.icons['fa_cube'], + 'blueman-manager': nf.icons['fa_bluetooth'], + 'brave-browser': nf.icons['fa_globe'], + 'calibre-gui': nf.icons['fa_book'], + 'chromium': nf.icons['fa_chrome'], + 'cities.x64': nf.icons['md_city'], + 'clonehero': nf.icons['md_guitar_electric'], + 'code-oss': nf.icons['fa_code'], + 'vscodium': nf.icons['fa_code'], + 'cutter': nf.icons['fa_bug'], + 'darktable': nf.icons['md_camera'], + 'discord': nf.icons['fa_comment'], + 'dwarf_fortress': nf.icons['fa_fort_awesome'], + 'easyeffects': nf.icons['fa_music'], + 'evince': nf.icons['fa_file_pdf_o'], + 'factorio': nf.icons['fa_cog'], + 'feh': nf.icons['md_image'], + 'firefox': nf.icons['fa_firefox'], + 'firefoxdeveloperedition': nf.icons['fa_firefox'], + 'ghb': nf.icons['md_video'], + 'gimp': nf.icons['fa_paint_brush'], + 'gimp-2.10': nf.icons['fa_paint_brush'], + 'gparted': nf.icons['fa_hdd_o'], + 'gsmartcontrol': nf.icons['fa_hdd_o'], + 'hoppscotch-app': nf.icons['md_ufo'], + 'inkscape': nf.icons['md_fountain_pen_tip'], + 'insomnium': nf.icons['md_graphql'], + 'kcharselect': nf.icons['fa_font'], + 'kicad': nf.icons['fa_microchip'], + 'ksp.x86_64': nf.icons['fa_space_shuttle'], + 'ledger live': nf.icons['md_wallet'], + 'libreoffice-calc': nf.icons['fa_file_excel_o'], + 'libreoffice-impress': nf.icons['fa_file_powerpoint_o'], + 'libreoffice-writer': nf.icons['fa_file_word_o'], + 'lutris': nf.icons['fa_gamepad'], + 'mpv': nf.icons['md_video'], + 'polymc': nf.icons['md_minecraft'], + 'minecraft': nf.icons['md_minecraft'], + 'mupdf': nf.icons['fa_file_pdf_o'], + 'nemiver': nf.icons['fa_bug'], + 'nm-connection-editor': nf.icons['fa_wifi'], + 'obs': nf.icons['md_video'], + 'openlens': nf.icons['md_kubernetes'], + 'org.gnome.nautilus': nf.icons['fa_folder'], + 'pavucontrol': nf.icons['fa_volume_up'], + 'picard': nf.icons['fa_music'], + 'qbittorrent': nf.icons['fa_download'], + 'qtcreator': nf.icons['fa_code'], + 'seahorse': nf.icons['fa_lock'], + 'simplescreenrecorder': nf.icons['md_video'], + 'slack': nf.icons['fa_slack'], + 'spotify': nf.icons['fa_spotify'], + 'sqlitebrowser': nf.icons['fa_database'], + 'steam': nf.icons['md_steam'], + 'steam_app_671860': nf.icons['md_pistol'], # battlebit + 'steam_app_1336490': nf.icons['fae_storm'], # against the storm + 'surviving mars': nf.icons['fa_rocket'], + 'telegram-desktop': nf.icons['fa_telegram'], + 'terraria.bin.x86_64': nf.icons['fa_tree'], + 'thunderbird': nf.icons['fa_envelope'], + 'vim': nf.icons['fa_code'], + 'virtualbox manager': nf.icons['fa_desktop'], + 'vlc': nf.icons['md_video'], + 'wireshark': nf.icons['md_shark_fin'], + 'zathura': nf.icons['fa_file_pdf_o'], + 'zenity': nf.icons['fa_window_maximize'], +} + +# This icon is used for any application not in the list above +DEFAULT_ICON = '*' + + +# Used so that we can keep the workspace's name when we add icons to it. +# Returns a dictionary with the 'num' and 'icons' keys +# Any field that's missing in @name will be None in the returned dict +def parse_workspace_number(name: str) -> str | None: + match = re.match( + r'(?P\d+):? ?(?P.+)?', + name, + ) + if match is None: + return None + + return match.groupdict()['num'] + + +# Given a dictionary with 'num', 'icons', +# return the formatted name by concatenating them together. +def construct_workspace_name(num: str, icons: list[str] | None) -> str: + new_name: str = str(num) + if icons: + new_name += ':' + for icon in icons: + new_name += ' ' + icon + + return new_name + + +# Returns an array of the values for the given property from xprop. This +# requires xorg-xprop to be installed. +def xprop(win_id, property): + try: + prop = proc.check_output( + ['xprop', '-id', str(win_id), property], + stderr=proc.DEVNULL, + ) + prop = prop.decode('utf-8') + return re.findall('"([^"]+)"', prop) + + except proc.CalledProcessError: + print(f'Unable to get property for window "{win_id}"') + return None + + +def icon_for_window(window) -> str: + app_id = window.app_id + if window.app_id is None or window.app_id == '': + classes: list[str] | None = xprop(window.window, 'WM_CLASS') + if classes is None or len(classes) == 0: + print(f'No class set for window: {xprop(window.window, "WM_NAME")}') + return DEFAULT_ICON + else: + app_id = classes[1].lower() + else: + app_id = window.app_id.lower() + + if 'minecraft' in app_id: + return WINDOW_ICONS["minecraft"] + + if app_id in WINDOW_ICONS: + return WINDOW_ICONS[app_id] + else: + print(f'No icon configured for: {app_id}') + + return DEFAULT_ICON + + +# renames all workspaces based on the windows present +def rename_workspaces(i3) -> None: + for workspace in i3.get_tree().workspaces(): + if workspace.num == -1: + continue + + num = parse_workspace_number(workspace.name) + icons = [ + icon_for_window(w) for w in workspace.leaves() + ] + if num is None: + new_name = str(workspace.num) + else: + new_name = construct_workspace_name(num, icons) + + i3.command(f'rename workspace "{workspace.name}" to "{new_name}"') + + +# rename workspaces to just numbers +# called on exit to indicate that this script is no longer running. +def undo_window_renaming(i3) -> None: + for workspace in i3.get_tree().workspaces(): + num = parse_workspace_number(workspace.name) + icons = None + if num is None: + new_name = str(workspace.num) + else: + new_name = construct_workspace_name(num, icons) + + i3.command(f'rename workspace "{workspace.name}" to "{new_name}"') + + i3.main_quit() + + +def get_signal_handler(i3) -> Callable[[int, FrameType | None], Any]: + def signal_handler(*_) -> None: + undo_window_renaming(i3) + + return signal_handler + + +def main(): + i3 = Connection() + + # exit gracefully when ctrl+c is pressed + for sig in [signal.SIGINT, signal.SIGTERM]: + signal.signal(sig, get_signal_handler(i3)) + + # call rename_workspaces() for relevant window events + def window_event_handler(i3, e) -> None: + if e.change in ['new', 'close', 'move']: + rename_workspaces(i3) + + i3.on('window', window_event_handler) + rename_workspaces(i3) + i3.main() + + +if __name__ == '__main__': + main() diff --git a/dot_config/sway/config.tmpl b/dot_config/sway/config.tmpl new file mode 100644 index 0000000..2872788 --- /dev/null +++ b/dot_config/sway/config.tmpl @@ -0,0 +1,264 @@ +# vim: ft=swayconfig ts=4 sts=4 sw=4 noet + +set $foreground #ebdbb2 +set $background #282828 +set $color0 #1d2021 +set $color1 #cc241d +set $color2 #98971a +set $color3 #d79921 +set $color4 #458588 +set $color5 #b16286 +set $color6 #689d6a +set $color7 #a89984 +set $color8 #665c54 +set $color9 #fb4934 +set $color10 #b8bb26 +set $color11 #fabd2f +set $color12 #83a598 +set $color13 #d3869b +set $color14 #8ec07c +set $color15 #fbf1c7 +set $color16 #d65d0e +set $color17 #fe8019 + +set $mod Mod4 +set $left h +set $down n +set $up e +set $right i + +set $telegram "" +set $firefox "" +set $thunderbird "" + +# Font for window titles. Will also be used by the bar unless a different font +# is used in the bar {} block below. +font pango:MonoFont {{ .sway.font_size }} + +#class background border text split +client.focused $color8 $color8 $foreground $color7 +client.focused_inactive $background $background $color7 $color0 +client.unfocused $color0 $background $color8 $color0 +client.urgent $color1 $color1 $foreground $color1 +client.background $background + +# Use Mouse+$mod to drag floating windows to their wanted position +floating_modifier $mod + +# kill focused window +bindsym $mod+Shift+q kill + +# change focus +bindsym $mod+$left focus left +bindsym $mod+$down focus down +bindsym $mod+$up focus up +bindsym $mod+$right focus right + +# move focused window +bindsym $mod+Shift+$left move left +bindsym $mod+Shift+$down move down +bindsym $mod+Shift+$up move up +bindsym $mod+Shift+$right move right + +# move workspace to other outputs +bindsym $mod+Control+$left move workspace to output left +bindsym $mod+Control+$down move workspace to output down +bindsym $mod+Control+$up move workspace to output up +bindsym $mod+Control+$right move workspace to output right + +# split in horizontal orientation +bindsym $mod+c split h + +# split in vertical orientation +bindsym $mod+v split v + +# enter fullscreen mode for the focused container +bindsym $mod+f fullscreen toggle + +# change container layout (tabbed, toggle split) +bindsym $mod+s layout toggle split +bindsym $mod+t layout tabbed + +# toggle tiling / floating +bindsym $mod+Shift+space floating toggle +bindsym $mod+Shift+BackSpace floating toggle +bindsym $mod+g focus mode_toggle +bindsym $mod+a focus parent + +bindsym $mod+Tab workspace next +bindsym $mod+Shift+Tab workspace prev + +# 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+p workspace $telegram +bindsym $mod+w workspace $firefox +bindsym $mod+m workspace $thunderbird + +# 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+p move container to workspace $telegram +bindsym $mod+Shift+w move container to workspace $firefox +bindsym $mod+Shift+m move container to workspace $thunderbird +bindsym $mod+Shift+r reload + +# start a terminal +bindsym $mod+Return exec alacritty + +# rofi bindings +bindsym $mod+space exec "rofi -show drun" +bindsym $mod+BackSpace exec "rofi -show drun" +bindsym $mod+j exec "rofi -show emoji" + +# Screen locking +# exec --no-startup-id xss-lock --transfer-sleep-lock -- i3lock -e -i ~/pictures/wallpapers/blue_screen.png -t --nofork +# bindsym $mod+Control+l exec loginctl lock-session +bindsym $mod+Control+l exec "i3lock -i ~/pictures/wallpapers/oslo_radhus.png -t" + +bindsym $mod+semicolon exec "systemctl suspend" + +mode "resize" { + bindsym h resize shrink width 10 px or 10 ppt + bindsym n resize shrink height 10 px or 10 ppt + bindsym e resize grow height 10 px or 10 ppt + bindsym i resize grow width 10 px or 10 ppt + + # back to normal: Enter or Escape + bindsym Return mode "default" + bindsym Escape mode "default" +} +bindsym $mod+r mode "resize" + +# Pulse Audio controls +bindsym XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume $(pactl info | \grep "Default Sink:" | cut -d ' ' -f 3) +2% +bindsym XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume $(pactl info | \grep "Default Sink:" | cut -d ' ' -f 3) -2% +bindsym Shift+XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume $(pactl info | \grep "Default Sink:" | cut -d ' ' -f 3) +8% +bindsym Shift+XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume $(pactl info | \grep "Default Sink:" | cut -d ' ' -f 3) -8% +bindsym XF86AudioMute exec --no-startup-id pactl set-sink-mute $(pactl info | \grep "Default Sink:" | cut -d ' ' -f 3) toggle +bindsym XF86AudioMedia exec --no-startup-id pactl set-source-mute $(pactl info | \grep "Default Source:" | cut -d ' ' -f 3) toggle + +# Sreen brightness controls +bindsym XF86MonBrightnessUp exec light -A 10 +bindsym XF86MonBrightnessDown exec light -U 10 +bindsym Shift+XF86MonBrightnessUp exec light -A 1 +bindsym Shift+XF86MonBrightnessDown exec light -U 1 + +# Media controls +bindsym XF86AudioPlay exec "playerctl-wrapper play-pause" +bindsym XF86AudioPause exec "playerctl-wrapper play-pause" +bindsym XF86AudioNext exec "playerctl-wrapper next" +bindsym XF86AudioPrev exec "playerctl-wrapper previous" + +# Screenshot controls +bindsym Print exec "pyazo --no-save" +bindsym Shift+Print exec "maim /home/pavle/pictures/screenshot.png" +bindsym $mod+Print exec "pyazo -d" + +# Additional bindings +{{ .sway.additional_bindings }} + +# Set default workspace layout to tabbed +workspace_layout tabbed + +# Auto-assign programs to certain workspaces +assign [app_id="(?i)firefox"] $firefox +assign [app_id="(?i)chromium"] $firefox +assign [app_id="org.telegram.desktop"] $telegram +assign [app_id="thunderbird"] $thunderbird +assign [app_id="(?i)slack"] $telegram +for_window [app_id="Lxappearance"] floating enable +for_window [app_id="tidal-hifi"] move to workspace $telegram + +# new_window pixel 2 +default_border pixel 2 +# default_border none +hide_edge_borders --i3 smart + +output HDMI-A-1 { + mode 1920x1080@60.00Hz + pos 2560 535 + bg /home/pavle/pictures/wallpapers/oslo_radhus.jpg fill +} + +output DP-1 { + mode 2560x1440@169.83Hz + transform 180 + pos 0 0 + bg /home/pavle/pictures/wallpapers/oslo_radhus.jpg fill +} + +input 1133:16505:Logitech_G_Pro { + accel_profile flat + pointer_accel 0 +} + + +# Startup +exec_always python ~/.config/sway/autoname-workspaces.py >/dev/null 2>&1 & +exec telegram-desktop >/dev/null 2>&1 & +{{ if .sway.nm_applet }}exec nm-applet >/dev/null 2>&1 &{{end}} +exec gtk-launch firefox >/dev/null 2>&1 & +exec thunderbird >/dev/null 2>&1 & +exec slack >/dev/null 2>&1 & +exec flatpak run com.mastermindzh.tidal-hifi >/dev/null 2>&1 & + +# Keyboard layout controls +set $mode_kb_select (1) US, (2) Colemak, (3) Latin, (4) Cyrillic, (5) DE +mode "$mode_kb_select" { + bindsym 1 exec --no-startup-id "setxkbmap -layout us", mode "default" + bindsym 2 exec --no-startup-id "setxkbmap -layout custom -variant colemak -option ctrl:swapcaps", mode "default" + bindsym 3 exec --no-startup-id "setxkbmap -layout rs -variant latin", mode "default" + bindsym 4 exec --no-startup-id "setxkbmap -layout rs", mode "default" + bindsym 5 exec --no-startup-id "setxkbmap -layout de", mode "default" + bindsym Escape mode "default" +} +bindsym $mod+F12 mode "$mode_kb_select" + +bar { + position top + # padding -2px 0 -1px + tray_output none + status_command ~/.local/share/bumblebee-status/bumblebee-status -c ~/.config/bumblebee-status/top.toml + bindsym button4 nop + bindsym button5 nop + colors { + background $color0 + focused_workspace $color8 $color8 $foreground + active_workspace $color8 $color8 $foreground + inactive_workspace $color8 $color0 $foreground + urgent_workspace $color9 $color1 $foreground + } +} + +bar { + position bottom + # padding -2px 0 -1px + tray_output {{ .sway.tray_output }} + status_command ~/.local/share/bumblebee-status/bumblebee-status -c ~/.config/bumblebee-status/bottom.toml + bindsym button4 nop + bindsym button5 nop + colors { + background $color0 + focused_workspace $color8 $color8 $foreground + active_workspace $color8 $color8 $foreground + inactive_workspace $color8 $color0 $foreground + urgent_workspace $color9 $color1 $foreground + } +}