#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SXWM_REPO="https://github.com/uint23/sxwm.git"
SXBAR_REPO="https://git.kristoffersson.info/repos/sxbar.git"
ST_REPO="https://git.suckless.org/st"
BUILD_DIR="/tmp/sxwm-sxbar-build"
echo "==> Installerar beroenden..."
sudo apt-get update -qq
sudo apt-get install -y \
git \
make \
gcc \
build-essential \
pkg-config \
libx11-dev \
libxinerama-dev \
libxrandr-dev \
libxcursor-dev \
libxft-dev \
libfontconfig1-dev \
libimlib2-dev \
xorg \
xinit \
firefox-esr \
pipewire-audio \
alsa-utils \
bluez \
iwd \
xinput \
patch \
wget \
curl \
unzip \
fontconfig \
wmctrl \
xdotool \
x11-utils \
pipx \
feh \
dmenu \
imagemagick \
bc \
geany \
gocryptfs \
fuse3 \
mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"
echo "==> Klonar sxwm..."
rm -rf sxwm
git clone --depth=1 "$SXWM_REPO" sxwm
echo "==> Applicerar sxwm-patchar..."
cd sxwm
patch -p1 < "$SCRIPT_DIR/cursor-dpi-fix.patch"
# Lägger till stöd för _NET_ACTIVE_WINDOW (behövs för sxbars taskbar-modul
# -- utan denna gör klick i taskbaren ingenting, sxwm har ingen annan
# extern mekanism för att fokusera ett specifikt fönster)
patch -p1 < "$SCRIPT_DIR/net-active-window.patch"
# Fixar _NET_WM_STRUT_PARTIAL-hantering vid monitorer av olika storlek --
# reservationer band tidigare bara till varje monitors x/y-spann, inte
# till panelfönstrets egen monitor, vilket kunde göra en monitors
# reserverade yta större än dess egen höjd/bredd (fönster öppnades då
# långt utanför synligt område, bara ram synlig)
patch -p1 < "$SCRIPT_DIR/multi-monitor-struts.patch"
# Windows tappar rätt monitor-koppling vid hotplug (t.ex. lid-display-watch
# som slår av/på eDP) -- client->mon är bara ett index i mons[], och blir
# meningslöst så fort monitor-antalet/ordningen ändras, vilket kan flytta
# fönster till fel skärm eller klämma in dem för litet. Räknar om mon-
# tillhörighet från varje fönsters faktiska position vid varje hotplug.
patch -p1 < "$SCRIPT_DIR/monitor-hotplug-remap.patch"
# apply_fullscreen() placerade fönstret på c->mon, som bara sätts en gång vid
# map-tid (till skärmen muspekaren råkade stå på) och aldrig hölls i synk med
# fönstrets verkliga position -- ett fönster flyttat av ett externt verktyg
# (xdotool, screensaver-launch) till en annan skärm innan det gjordes
# fullskärm hamnade då fullskärm på FEL skärm. Räknar om målskärmen från
# fönstrets faktiska geometri istället.
patch -p1 < "$SCRIPT_DIR/fullscreen-monitor-fix.patch"
# En bottom/right-panel (t.ex. sxbars secondary_bar) kunde hamna dold bakom
# vanliga fönster -- update_struts() räknar reserve_bottom/reserve_right som
# avstånd från *motsatt* skärmkant och behövde då hela skärmens bredd/höjd,
# men läste den ur scr_width/scr_height som bara uppdateras i update_mons()
# (start + monitor-hotplug). Blir de cachade värdena inaktuella räknas bara
# bottom/right fel (top/left beror aldrig på skärmstorleken, bara på
# monitorns egen x/y, och påverkades inte). Läser bredd/höjd direkt från
# X-servern i update_struts() istället för att lita på cachen.
patch -p1 < "$SCRIPT_DIR/strut-fresh-screen-size.patch"
echo "==> Bygger och installerar sxwm..."
make
sudo make install
cd "$BUILD_DIR"
echo "==> Klonar sxbar..."
rm -rf sxbar
git clone --depth=1 "$SXBAR_REPO" sxbar
echo "==> Bygger och installerar sxbar..."
cd sxbar
make
sudo make install
cd "$BUILD_DIR"
echo "==> Klonar st..."
rm -rf st
git clone --depth=1 "$ST_REPO" st
echo "==> Applicerar st-patchar (delkey, scrollback, clipboard, boxdraw_v2, vertcenter, alpha, blinking_cursor, bold-is-not-bright, anysize, histsize-5000, kitty-graphics-protocol)..."
cd st
for p in "$SCRIPT_DIR"/st-patches/*.patch; do
patch -p1 < "$p"
done
cp "$SCRIPT_DIR/config/st/config.def.h" config.def.h
echo "==> Bygger och installerar st..."
make
sudo make install
echo "==> Installerar icat (visa bilder i st via kitty graphics-protokollet)..."
mkdir -p "$HOME/.local/bin"
cp icat-mini.sh "$HOME/.local/bin/icat"
chmod +x "$HOME/.local/bin/icat"
echo " icat-mini.sh -> ~/.local/bin/icat"
cd "$BUILD_DIR"
echo "==> Kopierar konfigfiler..."
mkdir -p "$HOME/.config/sxbar"
copy_config() {
local src="$SCRIPT_DIR/config/$1"
local dst="$HOME/.config/$2"
mkdir -p "$(dirname "$dst")"
if [ -f "$dst" ]; then
cp "$dst" "$dst.bak"
echo " $2 -> backup sparad: ~/.config/$2.bak"
fi
cp "$src" "$dst"
echo " $2 -> ~/.config/$2"
}
copy_config sxwmrc sxwmrc
copy_config sxbarc sxbar/sxbarc
copy_config geany/geany.conf geany/geany.conf
copy_config geany/colorschemes/gruvbox.conf geany/colorschemes/gruvbox.conf
echo "==> Installerar Gruvbox GTK-tema (för Geanys gränssnitt, inte bara editorn)..."
mkdir -p "$HOME/.local/share/themes/Gruvbox-Dark/gtk-3.0"
cp "$SCRIPT_DIR/themes/Gruvbox-Dark/gtk-3.0/gtk.css" "$HOME/.local/share/themes/Gruvbox-Dark/gtk-3.0/gtk.css"
cp "$SCRIPT_DIR/themes/Gruvbox-Dark/index.theme" "$HOME/.local/share/themes/Gruvbox-Dark/index.theme"
echo " Gruvbox-Dark -> ~/.local/share/themes/Gruvbox-Dark"
echo "==> Installerar geany-wrapper..."
# Sätter GTK_THEME=Gruvbox-Dark oavsett hur Geany startas. Ett bash-alias
# räcker inte -- dmenu/sxwm kör aldrig ~/.bashrc, men ärver PATH från
# ~/.xinitrc där ~/.local/bin ligger först, så denna skuggar /usr/bin/geany.
cp "$SCRIPT_DIR/bin/geany" "$HOME/.local/bin/geany"
chmod +x "$HOME/.local/bin/geany"
echo " geany -> ~/.local/bin/geany"
echo "==> Installerar Geany .desktop-override (samma tema från app-startare)..."
mkdir -p "$HOME/.local/share/applications"
cp "$SCRIPT_DIR/applications/geany.desktop" "$HOME/.local/share/applications/geany.desktop"
echo " geany.desktop -> ~/.local/share/applications/geany.desktop"
echo "==> Kopierar skrivbordsbakgrund..."
mkdir -p "$HOME/BG"
cp -n "$SCRIPT_DIR/BG/"* "$HOME/BG/"
echo " BG/ -> ~/BG/"
echo ""
echo "==> Konfigurering av ~/.xinitrc"
echo ""
read -p "Vill du skriva över ~/.xinitrc? (j/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Jj]$ ]]; then
if [ -f "$HOME/.xinitrc" ]; then
cp "$HOME/.xinitrc" "$HOME/.xinitrc.bak"
echo " Backup skapad: ~/.xinitrc.bak"
fi
cat > "$HOME/.xinitrc" << 'EOF'
#!/bin/bash
# ~/.bash_profile sourcar inte ~/.profile/~/.bashrc, så PATH-tillägget för
# ~/.local/bin når annars aldrig sxwm eller dess "exec"-kommandon
# (mouse-settings, lid-display-watch m.fl. failar tyst utan denna rad)
export PATH="$HOME/.local/bin:$PATH"
# Stänga av skärmarna efter 5 min
xset s 300 300
xset dpms 300 300 300
# Stäng av pip-ljudet (bell)
xset b off
# Svenskt tangentbord
setxkbmap se
exec sxwm
EOF
chmod +x "$HOME/.xinitrc"
echo " ~/.xinitrc skapad"
else
echo "Lägg manuellt till i ~/.xinitrc:"
echo ""
echo " xset s 300 300"
echo " xset dpms 300 300 300"
echo " setxkbmap se"
echo " exec sxwm"
fi
echo "==> Installerar lidsuspend..."
mkdir -p "$HOME/.local/bin"
cp "$SCRIPT_DIR/bin/lidsuspend" "$HOME/.local/bin/lidsuspend"
chmod +x "$HOME/.local/bin/lidsuspend"
echo " lidsuspend -> ~/.local/bin/lidsuspend"
echo "==> Installerar mouse-settings..."
mkdir -p "$HOME/.local/bin"
cp "$SCRIPT_DIR/bin/mouse-settings" "$HOME/.local/bin/mouse-settings"
chmod +x "$HOME/.local/bin/mouse-settings"
echo " mouse-settings -> ~/.local/bin/mouse-settings"
echo "==> Installerar status..."
mkdir -p "$HOME/.local/bin"
cp "$SCRIPT_DIR/bin/status" "$HOME/.local/bin/status"
chmod +x "$HOME/.local/bin/status"
echo " status -> ~/.local/bin/status"
echo "==> Installerar gcfs (minimal gocryptfs-valvhanterare)..."
mkdir -p "$HOME/.local/bin"
cp "$SCRIPT_DIR/bin/gcfs" "$HOME/.local/bin/gcfs"
chmod +x "$HOME/.local/bin/gcfs"
# Lägg bara exempel-configen om ingen redan finns -- gcfs skapar en identisk
# vid första körning, och en riktig vaults.conf ska aldrig skrivas över.
if [ ! -f "$HOME/.config/gcfs/vaults.conf" ]; then
mkdir -p "$HOME/.config/gcfs"
cp "$SCRIPT_DIR/config/gcfs/vaults.conf" "$HOME/.config/gcfs/vaults.conf"
echo " gcfs -> ~/.local/bin/gcfs, vaults.conf -> ~/.config/gcfs/"
else
echo " gcfs -> ~/.local/bin/gcfs (behöll befintlig ~/.config/gcfs/vaults.conf)"
fi
echo "==> Installerar tte (terminaltexteffects) via pipx..."
pipx install terminaltexteffects
pipx ensurepath
echo "==> Installerar screensaver..."
mkdir -p "$HOME/.local/bin"
cp "$SCRIPT_DIR/bin/screensaver" "$SCRIPT_DIR/bin/screensaverd" "$SCRIPT_DIR/bin/screensaver-launch" "$SCRIPT_DIR/bin/screensaver-toggle" "$SCRIPT_DIR/bin/screensaver.txt" "$HOME/.local/bin/"
chmod +x "$HOME/.local/bin/screensaver" "$HOME/.local/bin/screensaverd" "$HOME/.local/bin/screensaver-launch" "$HOME/.local/bin/screensaver-toggle"
echo " screensaver, screensaverd, screensaver-launch, screensaver-toggle, screensaver.txt -> ~/.local/bin/"
echo ""
echo "==> Automatisk avstängning av interna skärmen vid stängt lock"
echo ""
read -p "Installera lid-display-watch (stänger av interna skärmen när locket stängs, men bara om en extern skärm är ansluten)? (j/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Jj]$ ]]; then
cp "$SCRIPT_DIR/bin/internal-display" "$HOME/.local/bin/internal-display"
chmod +x "$HOME/.local/bin/internal-display"
cp "$SCRIPT_DIR/bin/lid-display-watch" "$HOME/.local/bin/lid-display-watch"
chmod +x "$HOME/.local/bin/lid-display-watch"
echo " internal-display, lid-display-watch -> ~/.local/bin/"
if ! grep -qF 'exec : "lid-display-watch"' "$HOME/.config/sxwmrc" 2>/dev/null; then
echo 'exec : "lid-display-watch"' >> "$HOME/.config/sxwmrc"
echo " lid-display-watch tillagd i ~/.config/sxwmrc"
fi
else
echo " Hoppar över lid-display-watch"
fi
echo ""
echo "==> Stänga av locket ska inte göra något (ingen suspend/hibernate)"
echo ""
read -p "Sätta HandleLidSwitch=ignore i /etc/systemd/logind.conf? (j/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Jj]$ ]]; then
LOGIND_CONF="/etc/systemd/logind.conf"
if grep -q '^HandleLidSwitch=igonre$' "$LOGIND_CONF" 2>/dev/null; then
# Stavfel som gjort att systemd-logind ignorerat raden och fallit
# tillbaka på det inbyggda default-värdet ("suspend") istället
sudo sed -i 's/^HandleLidSwitch=igonre$/HandleLidSwitch=ignore/' "$LOGIND_CONF"
echo " HandleLidSwitch=igonre -> ignore (rättade stavfel)"
elif grep -q '^HandleLidSwitch=' "$LOGIND_CONF" 2>/dev/null; then
echo " HandleLidSwitch redan satt, rör inte: $(grep '^HandleLidSwitch=' "$LOGIND_CONF")"
else
echo 'HandleLidSwitch=ignore' | sudo tee -a "$LOGIND_CONF" > /dev/null
echo " HandleLidSwitch=ignore tillagd"
fi
echo " Kräver omstart för att aktiveras. Kör INTE 'systemctl restart"
echo " systemd-logind' live -- på den här maskinen (X startas manuellt"
echo " via startx/.xinitrc, ingen display manager) har det hängt hela"
echo " skrivbordet och krävt en hård omstart."
else
echo " Hoppar över HandleLidSwitch"
fi
echo ""
echo "==> Förhindra att Bluetooth tappar anslutningar vid systeminaktivitet"
echo ""
read -p "Installera udev-regel som stänger av USB-autosuspend för Bluetooth-adaptern? (j/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Jj]$ ]]; then
# Specifik för den här laptopens BT-chip (Qualcomm Atheros 0cf3:e500).
# Annan hårdvara: hitta idVendor/idProduct via `lsusb`, matcha mot
# /sys/class/bluetooth/hci0 -> realpath för att hitta rätt usb-enhet.
sudo cp "$SCRIPT_DIR/udev/71-bluetooth-no-autosuspend.rules" /etc/udev/rules.d/71-bluetooth-no-autosuspend.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --attr-match=idVendor=0cf3 --attr-match=idProduct=e500
echo " 71-bluetooth-no-autosuspend.rules -> /etc/udev/rules.d/"
else
echo " Hoppar över Bluetooth-udev-regeln"
fi
echo "==> Konfigurerar skärmsläckning på TTY1 (innan inloggning/efter utloggning)..."
GRUB_FILE="/etc/default/grub"
if grep -q 'consoleblank=' "$GRUB_FILE" 2>/dev/null; then
echo " consoleblank redan konfigurerad i $GRUB_FILE, hoppar över"
else
sudo sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT=/ s/"$/ consoleblank=300"/' "$GRUB_FILE"
sudo update-grub
echo " consoleblank=300 tillagt (5 min), kräver omstart för att aktiveras"
fi
echo "==> Stänger av PC-speaker beep..."
echo "blacklist pcspkr" | sudo tee /etc/modprobe.d/nobeep.conf > /dev/null
sudo rmmod pcspkr 2>/dev/null || true
echo ""
read -p "Installera Rust (rustup) och bygga/installera bluetui (bluetooth-hantering)? (j/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Jj]$ ]]; then
echo "==> Tar bort ev. gammal rustc/cargo från apt (Debian har ofta för gammal version)..."
RUST_APT_PKGS=$(dpkg-query -W -f='${Package}\n' 'rustc' 'cargo' 'libstd-rust-dev' 'rust-llvm' 'libstd-rust-*' 2>/dev/null || true)
if [ -n "$RUST_APT_PKGS" ]; then
sudo apt-get purge -y $RUST_APT_PKGS
sudo apt-get autoremove --purge -y
fi
echo "==> Installerar rustup..."
if ! command -v rustup >/dev/null 2>&1 && [ ! -x "$HOME/.cargo/bin/rustup" ]; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
fi
# shellcheck disable=SC1091
source "$HOME/.cargo/env"
echo "==> Bygger och installerar bluetui..."
rm -rf "$BUILD_DIR/bluetui"
git clone --depth=1 https://github.com/pythops/bluetui "$BUILD_DIR/bluetui"
cd "$BUILD_DIR/bluetui"
cargo build --release
sudo cp target/release/bluetui /usr/local/bin/
cd "$BUILD_DIR"
else
echo " Hoppar över Rust och bluetui"
fi
echo "==> Installerar JetBrainsMono Nerd Font..."
FONT_DIR="$HOME/.local/share/fonts/JetBrainsMono"
mkdir -p "$FONT_DIR"
FONT_URL="https://github.com/ryanoasis/nerd-fonts/releases/latest/download/JetBrainsMono.zip"
wget -q --show-progress -O /tmp/JetBrainsMono.zip "$FONT_URL"
unzip -o /tmp/JetBrainsMono.zip -d "$FONT_DIR" '*.ttf'
rm /tmp/JetBrainsMono.zip
fc-cache -f
echo " JetBrainsMono Nerd Font installerad"
echo ""
read -p "Lägga till automatisk start av X (sxwm) vid inloggning på TTY1? (j/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Jj]$ ]]; then
echo "==> Konfigurerar autostart av X på TTY1..."
BASH_PROFILE="$HOME/.bash_profile"
AUTOSTART='if [ -z "$DISPLAY" ] && [ "$(tty)" = "/dev/tty1" ]; then exec startx; fi'
if grep -qF 'exec startx' "$BASH_PROFILE" 2>/dev/null; then
echo " ~/.bash_profile redan konfigurerad, hoppar över"
else
echo "" >> "$BASH_PROFILE"
echo "$AUTOSTART" >> "$BASH_PROFILE"
echo " ~/.bash_profile uppdaterad"
fi
else
echo " Hoppar över autostart av X"
fi
echo "==> Aktiverar PipeWire..."
systemctl --user enable --now pipewire pipewire-pulse wireplumber
echo ""
echo "Installation slutförd!"