Add session manager for tmux.

This will open a tmux session for each monitor.
Add helper script for tmux actions.
This commit is contained in:
2025-09-22 15:26:42 +02:00
parent c4df56cb47
commit 8e563fec51
8 changed files with 622 additions and 11 deletions

View File

@@ -34,10 +34,6 @@ exec --no-startup-id xss-lock --transfer-sleep-lock -- i3lock -f --nofork
# and nm-applet is a desktop environment-independent system tray GUI for it.
exec --no-startup-id nm-applet
# Wallpaper rotation script
exec --no-startup-id ~/.config/i3/wallpaper-rotate.sh --once
exec --no-startup-id ~/.config/i3/wallpaper-rotate.sh --daemon 1800
# Use pactl to adjust volume in PulseAudio.
set $refresh_i3status killall -SIGUSR1 i3status
bindsym XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ +10% && $refresh_i3status
@@ -48,8 +44,15 @@ bindsym XF86AudioMicMute exec --no-startup-id pactl set-source-mute @DEFAULT_SOU
# Use Mouse+$mod to drag floating windows to their wanted position
floating_modifier $mod
# start a terminal
bindsym $mod+Return exec alacritty
# start a terminal with smart monitor/workspace-aware tmux sessions
bindsym $mod+Return exec --no-startup-id /home/jens/.config/i3/smart-terminal.sh
# fallback simple terminal (without smart sessions)
bindsym $mod+Shift+Return exec --no-startup-id /home/jens/.config/i3/simple-terminal.sh
# tmux session management keybindings
bindsym $mod+Shift+t exec --no-startup-id /home/jens/.cargo/bin/alacritty -e /home/jens/.config/i3/tmux-session-manager.sh
bindsym $mod+Control+t exec --no-startup-id /home/jens/.config/i3/tmux-session-manager.sh list | rofi -dmenu -p "Tmux Sessions" -width 60
# kill focused window
bindsym $mod+Shift+q kill
@@ -63,6 +66,23 @@ bindsym $mod+Shift+q kill
# bindcode $mod+40 exec --no-startup-id i3-dmenu-desktop
bindsym $mod+d exec rofi -show run
# Screenshot bindings
bindsym Print exec --no-startup-id ~/.local/bin/screenshot.sh full
bindsym Shift+Print exec --no-startup-id ~/.local/bin/screenshot.sh selection
bindsym $mod+Print exec --no-startup-id ~/.local/bin/screenshot.sh window
bindsym Control+Print exec --no-startup-id ~/.local/bin/screenshot.sh clipboard
bindsym Control+Shift+Print exec --no-startup-id ~/.local/bin/screenshot.sh clipboard-selection
# Alternative screenshot bindings for keyboards that send different codes
bindsym XF86ScreenSaver exec --no-startup-id ~/.local/bin/screenshot.sh full
bindsym Shift+XF86ScreenSaver exec --no-startup-id ~/.local/bin/screenshot.sh selection
bindsym $mod+XF86ScreenSaver exec --no-startup-id ~/.local/bin/screenshot.sh window
# Additional screenshot bindings with mod+s combinations
bindsym $mod+Shift+s exec --no-startup-id ~/.local/bin/screenshot.sh selection
bindsym $mod+Control+s exec --no-startup-id ~/.local/bin/screenshot.sh window
bindsym $mod+Control+Shift+s exec --no-startup-id ~/.local/bin/screenshot.sh full
# change focus
bindsym $mod+j focus left
bindsym $mod+k focus down
@@ -116,7 +136,7 @@ bindsym $mod+a focus parent
# Wallpaper controls
bindsym $mod+Shift+w exec --no-startup-id ~/.config/i3/wallpaper-rotate.sh --once
bindsym $mod+Shift+n exec --no-startup-id ~/.config/i3/wallpaper-rotate.sh --category nature
bindsym $mod+Control+s exec --no-startup-id ~/.config/i3/wallpaper-rotate.sh --category space
# bindsym $mod+Control+s exec --no-startup-id ~/.config/i3/wallpaper-rotate.sh --category space # Disabled - conflicts with screenshot binding
bindsym $mod+Control+c exec --no-startup-id ~/.config/i3/wallpaper-rotate.sh --category cityscape
# Define names for default workspaces for which we configure key bindings later on.

13
i3/simple-terminal.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/bash
# Simple fallback terminal launcher
# Uses alacritty directly without complex session detection
ALACRITTY="/home/jens/.cargo/bin/alacritty"
if [ -x "$ALACRITTY" ]; then
exec "$ALACRITTY"
else
# Fallback to xterm
exec xterm
fi

97
i3/smart-terminal.sh Executable file
View File

@@ -0,0 +1,97 @@
#!/bin/bash
# Smart terminal launcher with monitor-aware tmux sessions
# This script detects the current monitor and workspace to create/attach to appropriate tmux sessions
# Get the focused monitor using i3-msg
get_current_monitor() {
if command -v i3-msg &> /dev/null && i3-msg -t get_workspaces &>/dev/null; then
i3-msg -t get_workspaces 2>/dev/null | jq -r '.[] | select(.focused==true) | .output' 2>/dev/null
else
echo "default"
fi
}
# Get the current workspace number
get_current_workspace() {
if command -v i3-msg &> /dev/null && i3-msg -t get_workspaces &>/dev/null; then
i3-msg -t get_workspaces 2>/dev/null | jq -r '.[] | select(.focused==true) | .num' 2>/dev/null
else
echo "1"
fi
}
# Determine session name based on monitor and workspace
get_session_name() {
local monitor=$(get_current_monitor)
local workspace=$(get_current_workspace)
# Fallback if we can't get monitor/workspace info
if [ -z "$monitor" ] || [ "$monitor" = "null" ]; then
monitor="default"
fi
if [ -z "$workspace" ] || [ "$workspace" = "null" ]; then
workspace="1"
fi
# Clean monitor name (remove special characters)
monitor_clean=$(echo "$monitor" | sed 's/[^a-zA-Z0-9-]/_/g')
# Map monitors to logical screen names
case "$monitor_clean" in
"eDP_1"|"eDP1")
screen="laptop"
;;
"DVI_I_2_2"|"HDMI_1"|"HDMI1"|"DP_1"|"DP1")
screen="external1"
;;
"DVI_I_1_1"|"HDMI_2"|"HDMI2"|"DP_2"|"DP2")
screen="external2"
;;
"default")
screen="main"
;;
*)
screen="${monitor_clean}"
;;
esac
# Create session name: screen-workspace (e.g., laptop-1, external1-5)
if [ "$screen" = "main" ]; then
echo "main"
else
echo "${screen}-ws${workspace}"
fi
}
# Launch alacritty with tmux
launch_terminal() {
local session_name=$(get_session_name)
# Debug logging (remove after testing)
echo "Launching terminal with session: $session_name" >> /tmp/smart-terminal.log
# Check if alacritty exists
ALACRITTY_PATH="/home/jens/.cargo/bin/alacritty"
if [ ! -x "$ALACRITTY_PATH" ]; then
ALACRITTY_PATH=$(which alacritty 2>/dev/null)
if [ -z "$ALACRITTY_PATH" ]; then
# Fallback to xterm or another terminal
echo "Alacritty not found, falling back to xterm" >> /tmp/smart-terminal.log
xterm -e tmux new-session -A -s "$session_name" &
exit 0
fi
fi
# Check if tmux session exists
if tmux has-session -t "$session_name" 2>/dev/null; then
# Session exists, attach to it
"$ALACRITTY_PATH" --title "Terminal [$session_name]" -e tmux attach-session -t "$session_name" &
else
# Create new session with meaningful name
"$ALACRITTY_PATH" --title "Terminal [$session_name]" -e tmux new-session -s "$session_name" &
fi
}
# Main execution
launch_terminal

229
i3/tmux-session-manager.sh Executable file
View File

@@ -0,0 +1,229 @@
#!/bin/bash
# Advanced tmux session management for multi-monitor setup
# Provides utilities for managing monitor-specific tmux sessions
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# List all tmux sessions with their status
list_sessions() {
echo -e "${BLUE}=== Tmux Sessions by Screen ===${NC}"
# Group sessions by screen
for screen in laptop external1 external2; do
sessions=$(tmux list-sessions 2>/dev/null | grep "^${screen}-" | cut -d: -f1)
if [ ! -z "$sessions" ]; then
echo -e "\n${GREEN}${screen}:${NC}"
while IFS= read -r session; do
workspace=$(echo "$session" | cut -d- -f2)
attached=""
if tmux list-sessions 2>/dev/null | grep "^${session}:" | grep -q "(attached)"; then
attached=" ${YELLOW}[attached]${NC}"
fi
echo -e "${session}${attached}"
done <<< "$sessions"
fi
done
# Show other sessions
other_sessions=$(tmux list-sessions 2>/dev/null | grep -v -E "^(laptop|external1|external2)-" | cut -d: -f1)
if [ ! -z "$other_sessions" ]; then
echo -e "\n${BLUE}Other sessions:${NC}"
while IFS= read -r session; do
attached=""
if tmux list-sessions 2>/dev/null | grep "^${session}:" | grep -q "(attached)"; then
attached=" ${YELLOW}[attached]${NC}"
fi
echo -e "${session}${attached}"
done <<< "$other_sessions"
fi
}
# Kill all sessions for a specific screen
kill_screen_sessions() {
local screen=$1
if [ -z "$screen" ]; then
echo -e "${RED}Error: Please specify a screen (laptop, external1, external2)${NC}"
return 1
fi
sessions=$(tmux list-sessions 2>/dev/null | grep "^${screen}-" | cut -d: -f1)
if [ -z "$sessions" ]; then
echo -e "${YELLOW}No sessions found for screen: ${screen}${NC}"
return 0
fi
echo -e "${YELLOW}Killing all sessions for ${screen}...${NC}"
while IFS= read -r session; do
tmux kill-session -t "$session" 2>/dev/null
echo -e " ${RED}${NC} Killed: $session"
done <<< "$sessions"
}
# Rename a session
rename_session() {
local old_name=$1
local new_name=$2
if [ -z "$old_name" ] || [ -z "$new_name" ]; then
echo -e "${RED}Error: Usage: rename_session <old_name> <new_name>${NC}"
return 1
fi
if tmux has-session -t "$old_name" 2>/dev/null; then
tmux rename-session -t "$old_name" "$new_name"
echo -e "${GREEN}${NC} Renamed: $old_name$new_name"
else
echo -e "${RED}Error: Session '$old_name' not found${NC}"
return 1
fi
}
# Switch to a different session
switch_session() {
local session_name=$1
if [ -z "$session_name" ]; then
# Interactive session picker using fzf if available
if command -v fzf &> /dev/null; then
session_name=$(tmux list-sessions -F "#{session_name}" 2>/dev/null | fzf --prompt="Select session: ")
else
echo -e "${RED}Error: Please specify a session name${NC}"
list_sessions
return 1
fi
fi
if [ ! -z "$session_name" ] && tmux has-session -t "$session_name" 2>/dev/null; then
if [ -n "$TMUX" ]; then
tmux switch-client -t "$session_name"
else
tmux attach-session -t "$session_name"
fi
else
echo -e "${RED}Error: Session '$session_name' not found${NC}"
return 1
fi
}
# Create a new session for current monitor/workspace
new_session_here() {
# Source the functions from smart-terminal.sh
source /home/jens/.config/i3/smart-terminal.sh
local session_name=$(get_session_name)
if tmux has-session -t "$session_name" 2>/dev/null; then
echo -e "${YELLOW}Session '$session_name' already exists${NC}"
return 1
fi
tmux new-session -d -s "$session_name"
echo -e "${GREEN}${NC} Created new session: $session_name"
}
# Clean up detached sessions
cleanup_detached() {
local detached=$(tmux list-sessions 2>/dev/null | grep -v "(attached)" | cut -d: -f1)
if [ -z "$detached" ]; then
echo -e "${GREEN}No detached sessions to clean up${NC}"
return 0
fi
echo -e "${YELLOW}Found detached sessions:${NC}"
echo "$detached"
echo -e "\n${YELLOW}Kill all detached sessions? (y/N)${NC}"
read -r response
if [[ "$response" =~ ^[Yy]$ ]]; then
while IFS= read -r session; do
tmux kill-session -t "$session" 2>/dev/null
echo -e " ${RED}${NC} Killed: $session"
done <<< "$detached"
fi
}
# Main menu
show_menu() {
echo -e "${BLUE}=== Tmux Session Manager ===${NC}"
echo "1. List all sessions"
echo "2. Switch to session"
echo "3. Kill screen sessions"
echo "4. Rename session"
echo "5. Create session for current workspace"
echo "6. Cleanup detached sessions"
echo "7. Exit"
echo -n "Select option: "
}
# Main execution
case "$1" in
list)
list_sessions
;;
kill-screen)
kill_screen_sessions "$2"
;;
rename)
rename_session "$2" "$3"
;;
switch)
switch_session "$2"
;;
new)
new_session_here
;;
cleanup)
cleanup_detached
;;
*)
if [ -z "$1" ]; then
# Interactive mode
while true; do
show_menu
read -r option
case $option in
1) list_sessions ;;
2)
echo -n "Session name: "
read -r session
switch_session "$session"
;;
3)
echo -n "Screen (laptop/external1/external2): "
read -r screen
kill_screen_sessions "$screen"
;;
4)
echo -n "Old name: "
read -r old
echo -n "New name: "
read -r new
rename_session "$old" "$new"
;;
5) new_session_here ;;
6) cleanup_detached ;;
7) exit 0 ;;
*) echo -e "${RED}Invalid option${NC}" ;;
esac
echo
done
else
echo "Usage: $0 [command] [args]"
echo "Commands:"
echo " list - List all sessions"
echo " kill-screen <screen> - Kill all sessions for a screen"
echo " rename <old> <new> - Rename a session"
echo " switch <session> - Switch to a session"
echo " new - Create session for current workspace"
echo " cleanup - Cleanup detached sessions"
echo ""
echo "Run without arguments for interactive mode"
fi
;;
esac