hyprland: add rofi icons to window go/bring/replace pickers
Add desktop-entry icon lookup to the rofi window picker scripts, matching the XMonad setup's icon support. A shared helper script (window-icon-map.sh) builds a class→icon mapping from .desktop files and each picker uses rofi's dmenu icon protocol (\0icon\x1f). Also replaces the X11-only "rofi -show window" with a native Hyprland window picker using hyprctl clients. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,7 @@ monitor=,preferred,auto,1
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
# PROGRAMS
|
# PROGRAMS
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
$terminal = alacritty
|
$terminal = ghostty
|
||||||
$fileManager = dolphin
|
$fileManager = dolphin
|
||||||
$menu = rofi -show drun -show-icons
|
$menu = rofi -show drun -show-icons
|
||||||
$runMenu = rofi -show run
|
$runMenu = rofi -show run
|
||||||
@@ -284,7 +284,7 @@ bind = $modAlt, C, exec, ~/.config/hypr/scripts/raise-or-run.sh google-chrome go
|
|||||||
# Toggle scratchpads
|
# Toggle scratchpads
|
||||||
bind = $modAlt, E, exec, ~/.config/hypr/scripts/toggle-scratchpad.sh element '^Element$' - element-desktop
|
bind = $modAlt, E, exec, ~/.config/hypr/scripts/toggle-scratchpad.sh element '^Element$' - element-desktop
|
||||||
bind = $modAlt, G, exec, ~/.config/hypr/scripts/toggle-scratchpad.sh gmail '^google-chrome$' '.*@gmail.com.*Gmail.*' google-chrome-stable --new-window https://mail.google.com/mail/u/0/#inbox
|
bind = $modAlt, G, exec, ~/.config/hypr/scripts/toggle-scratchpad.sh gmail '^google-chrome$' '.*@gmail.com.*Gmail.*' google-chrome-stable --new-window https://mail.google.com/mail/u/0/#inbox
|
||||||
bind = $modAlt, H, exec, ~/.config/hypr/scripts/toggle-scratchpad.sh htop '^htop-scratch$' - alacritty --class htop-scratch --title htop -e htop
|
bind = $modAlt, H, exec, ~/.config/hypr/scripts/toggle-scratchpad.sh htop '^htop-scratch$' - ghostty --class=htop-scratch --title=htop -e htop
|
||||||
bind = $modAlt, M, exec, ~/.config/hypr/scripts/toggle-scratchpad.sh messages '^google-chrome$' '^Messages.*' google-chrome-stable --new-window https://messages.google.com/web/conversations
|
bind = $modAlt, M, exec, ~/.config/hypr/scripts/toggle-scratchpad.sh messages '^google-chrome$' '^Messages.*' google-chrome-stable --new-window https://messages.google.com/web/conversations
|
||||||
bind = $modAlt, K, exec, ~/.config/hypr/scripts/toggle-scratchpad.sh slack '^Slack$' - slack
|
bind = $modAlt, K, exec, ~/.config/hypr/scripts/toggle-scratchpad.sh slack '^Slack$' - slack
|
||||||
bind = $modAlt, S, exec, ~/.config/hypr/scripts/toggle-scratchpad.sh spotify '^spotify$' - spotify
|
bind = $modAlt, S, exec, ~/.config/hypr/scripts/toggle-scratchpad.sh spotify '^spotify$' - spotify
|
||||||
@@ -489,8 +489,8 @@ bind = $mainMod SHIFT, H, exec, ~/.config/hypr/scripts/workspace-move-to-empty.s
|
|||||||
# WINDOW MANAGEMENT
|
# WINDOW MANAGEMENT
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
# Go to window (rofi window switcher)
|
# Go to window (rofi window switcher with icons)
|
||||||
bind = $mainMod, G, exec, rofi -show window -show-icons
|
bind = $mainMod, G, exec, ~/.config/hypr/scripts/go-to-window.sh
|
||||||
|
|
||||||
# Bring window (move to current workspace)
|
# Bring window (move to current workspace)
|
||||||
bind = $mainMod, B, exec, ~/.config/hypr/scripts/bring-window.sh
|
bind = $mainMod, B, exec, ~/.config/hypr/scripts/bring-window.sh
|
||||||
@@ -576,7 +576,7 @@ exec-once = ~/.config/hypr/scripts/workspace-history.sh
|
|||||||
# Scratchpad applications (spawn on demand via keybinds)
|
# Scratchpad applications (spawn on demand via keybinds)
|
||||||
# exec-once = [workspace special:element silent] element-desktop
|
# exec-once = [workspace special:element silent] element-desktop
|
||||||
# exec-once = [workspace special:gmail silent] google-chrome-stable --new-window https://mail.google.com/mail/u/0/#inbox
|
# exec-once = [workspace special:gmail silent] google-chrome-stable --new-window https://mail.google.com/mail/u/0/#inbox
|
||||||
# exec-once = [workspace special:htop silent] alacritty --class htop-scratch --title htop -e htop
|
# exec-once = [workspace special:htop silent] ghostty --class=htop-scratch --title=htop -e htop
|
||||||
# exec-once = [workspace special:messages silent] google-chrome-stable --new-window https://messages.google.com/web/conversations
|
# exec-once = [workspace special:messages silent] google-chrome-stable --new-window https://messages.google.com/web/conversations
|
||||||
# exec-once = [workspace special:slack silent] slack
|
# exec-once = [workspace special:slack silent] slack
|
||||||
# exec-once = [workspace special:spotify silent] spotify
|
# exec-once = [workspace special:spotify silent] spotify
|
||||||
|
|||||||
@@ -1,30 +1,40 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Bring window to current workspace (like XMonad's bringWindow)
|
# Bring window to current workspace (like XMonad's bringWindow)
|
||||||
# Uses rofi to select a window and moves it to the current workspace
|
# Uses rofi with icons to select a window, then moves it here.
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Get current workspace
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/window-icon-map.sh"
|
||||||
|
|
||||||
CURRENT_WS=$(hyprctl activeworkspace -j | jq -r '.id')
|
CURRENT_WS=$(hyprctl activeworkspace -j | jq -r '.id')
|
||||||
|
|
||||||
# Get all windows and format for rofi
|
# Get windows on OTHER workspaces as TSV
|
||||||
WINDOWS=$(hyprctl clients -j | jq -r '.[] | select(.workspace.id >= 0 and .workspace.id != '"$CURRENT_WS"') | "\(.title) [\(.class)] - WS:\(.workspace.id) |\(.address)"')
|
WINDOW_DATA=$(hyprctl clients -j | jq -r --argjson cws "$CURRENT_WS" '
|
||||||
|
.[] | select(.workspace.id >= 0 and .workspace.id != $cws)
|
||||||
|
| [.address, .class, (.title | gsub("\t"; " ")), (.workspace.id | tostring)]
|
||||||
|
| @tsv')
|
||||||
|
|
||||||
if [ -z "$WINDOWS" ]; then
|
if [ -z "$WINDOW_DATA" ]; then
|
||||||
notify-send "Bring Window" "No windows on other workspaces"
|
notify-send "Bring Window" "No windows on other workspaces"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Show rofi menu
|
addresses=()
|
||||||
SELECTION=$(echo "$WINDOWS" | rofi -dmenu -i -p "Bring window" -format 's')
|
TMPFILE=$(mktemp)
|
||||||
|
trap 'rm -f "$TMPFILE"' EXIT
|
||||||
|
|
||||||
if [ -n "$SELECTION" ]; then
|
while IFS=$'\t' read -r address class title ws_id; do
|
||||||
# Extract the window address (after the last |)
|
icon=$(icon_for_class "$class")
|
||||||
ADDRESS=$(echo "$SELECTION" | sed 's/.*|//')
|
addresses+=("$address")
|
||||||
|
printf '%-20s %-40s WS:%s\0icon\x1f%s\n' \
|
||||||
|
"$class" "${title:0:40}" "$ws_id" "$icon"
|
||||||
|
done <<< "$WINDOW_DATA" > "$TMPFILE"
|
||||||
|
|
||||||
# Move window to current workspace
|
INDEX=$(rofi -dmenu -i -show-icons -p "Bring window" -format i < "$TMPFILE") || exit 0
|
||||||
|
|
||||||
|
if [ -n "$INDEX" ] && [ -n "${addresses[$INDEX]:-}" ]; then
|
||||||
|
ADDRESS="${addresses[$INDEX]}"
|
||||||
hyprctl dispatch movetoworkspace "$CURRENT_WS,address:$ADDRESS"
|
hyprctl dispatch movetoworkspace "$CURRENT_WS,address:$ADDRESS"
|
||||||
|
|
||||||
# Focus the window
|
|
||||||
hyprctl dispatch focuswindow "address:$ADDRESS"
|
hyprctl dispatch focuswindow "address:$ADDRESS"
|
||||||
fi
|
fi
|
||||||
|
|||||||
33
dotfiles/config/hypr/scripts/go-to-window.sh
Executable file
33
dotfiles/config/hypr/scripts/go-to-window.sh
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Go to a window selected via rofi (with icons from desktop entries).
|
||||||
|
# Replaces "rofi -show window" which doesn't work well on Wayland.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/window-icon-map.sh"
|
||||||
|
|
||||||
|
# Get all windows on regular workspaces as TSV
|
||||||
|
WINDOW_DATA=$(hyprctl clients -j | jq -r '
|
||||||
|
.[] | select(.workspace.id >= 0)
|
||||||
|
| [.address, .class, (.title | gsub("\t"; " ")), (.workspace.id | tostring)]
|
||||||
|
| @tsv')
|
||||||
|
|
||||||
|
[ -n "$WINDOW_DATA" ] || exit 0
|
||||||
|
|
||||||
|
addresses=()
|
||||||
|
TMPFILE=$(mktemp)
|
||||||
|
trap 'rm -f "$TMPFILE"' EXIT
|
||||||
|
|
||||||
|
while IFS=$'\t' read -r address class title ws_id; do
|
||||||
|
icon=$(icon_for_class "$class")
|
||||||
|
addresses+=("$address")
|
||||||
|
printf '%-20s %-40s WS:%s\0icon\x1f%s\n' \
|
||||||
|
"$class" "${title:0:40}" "$ws_id" "$icon"
|
||||||
|
done <<< "$WINDOW_DATA" > "$TMPFILE"
|
||||||
|
|
||||||
|
INDEX=$(rofi -dmenu -i -show-icons -p "Go to window" -format i < "$TMPFILE") || exit 0
|
||||||
|
|
||||||
|
if [ -n "$INDEX" ] && [ -n "${addresses[$INDEX]:-}" ]; then
|
||||||
|
hyprctl dispatch focuswindow "address:${addresses[$INDEX]}"
|
||||||
|
fi
|
||||||
@@ -4,8 +4,9 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Get current workspace and focused window
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
CURRENT_WS=$(hyprctl activeworkspace -j | jq -r '.id')
|
source "$SCRIPT_DIR/window-icon-map.sh"
|
||||||
|
|
||||||
FOCUSED=$(hyprctl activewindow -j | jq -r '.address')
|
FOCUSED=$(hyprctl activewindow -j | jq -r '.address')
|
||||||
|
|
||||||
if [ "$FOCUSED" = "null" ] || [ -z "$FOCUSED" ]; then
|
if [ "$FOCUSED" = "null" ] || [ -z "$FOCUSED" ]; then
|
||||||
@@ -13,21 +14,30 @@ if [ "$FOCUSED" = "null" ] || [ -z "$FOCUSED" ]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get all windows except focused
|
# Get all windows except focused as TSV
|
||||||
WINDOWS=$(hyprctl clients -j | jq -r ".[] | select(.workspace.id >= 0 and .address != \"$FOCUSED\") | \"\(.title) [\(.class)] - WS:\(.workspace.id) |\(.address)\"")
|
WINDOW_DATA=$(hyprctl clients -j | jq -r --arg focused "$FOCUSED" '
|
||||||
|
.[] | select(.workspace.id >= 0 and .address != $focused)
|
||||||
|
| [.address, .class, (.title | gsub("\t"; " ")), (.workspace.id | tostring)]
|
||||||
|
| @tsv')
|
||||||
|
|
||||||
if [ -z "$WINDOWS" ]; then
|
if [ -z "$WINDOW_DATA" ]; then
|
||||||
notify-send "Replace Window" "No other windows available"
|
notify-send "Replace Window" "No other windows available"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Show rofi menu
|
addresses=()
|
||||||
SELECTION=$(echo "$WINDOWS" | rofi -dmenu -i -p "Replace with" -format 's')
|
TMPFILE=$(mktemp)
|
||||||
|
trap 'rm -f "$TMPFILE"' EXIT
|
||||||
|
|
||||||
if [ -n "$SELECTION" ]; then
|
while IFS=$'\t' read -r address class title ws_id; do
|
||||||
# Extract the window address
|
icon=$(icon_for_class "$class")
|
||||||
ADDRESS=$(echo "$SELECTION" | sed 's/.*|//')
|
addresses+=("$address")
|
||||||
|
printf '%-20s %-40s WS:%s\0icon\x1f%s\n' \
|
||||||
|
"$class" "${title:0:40}" "$ws_id" "$icon"
|
||||||
|
done <<< "$WINDOW_DATA" > "$TMPFILE"
|
||||||
|
|
||||||
# Swap windows using hy3
|
INDEX=$(rofi -dmenu -i -show-icons -p "Replace with" -format i < "$TMPFILE") || exit 0
|
||||||
hyprctl dispatch hy3:movewindow "address:$ADDRESS"
|
|
||||||
|
if [ -n "$INDEX" ] && [ -n "${addresses[$INDEX]:-}" ]; then
|
||||||
|
hyprctl dispatch hy3:movewindow "address:${addresses[$INDEX]}"
|
||||||
fi
|
fi
|
||||||
|
|||||||
66
dotfiles/config/hypr/scripts/window-icon-map.sh
Executable file
66
dotfiles/config/hypr/scripts/window-icon-map.sh
Executable file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Source this file to get icon_for_class function.
|
||||||
|
# Builds a mapping from window class → freedesktop icon name
|
||||||
|
# by scanning .desktop files for StartupWMClass and Icon fields.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# source "$(dirname "$0")/window-icon-map.sh"
|
||||||
|
# icon=$(icon_for_class "google-chrome")
|
||||||
|
|
||||||
|
declare -A _WINDOW_ICON_MAP
|
||||||
|
|
||||||
|
_build_window_icon_map() {
|
||||||
|
local IFS=':'
|
||||||
|
local -a search_dirs=()
|
||||||
|
local dir
|
||||||
|
|
||||||
|
for dir in ${XDG_DATA_DIRS:-/run/current-system/sw/share:/usr/share:/usr/local/share}; do
|
||||||
|
[ -d "$dir/applications" ] && search_dirs+=("$dir/applications")
|
||||||
|
done
|
||||||
|
[ -d "$HOME/.local/share/applications" ] && search_dirs+=("$HOME/.local/share/applications")
|
||||||
|
[ ${#search_dirs[@]} -eq 0 ] && return
|
||||||
|
|
||||||
|
# Expand globs per-directory so the pattern works correctly
|
||||||
|
local -a desktop_files=()
|
||||||
|
for dir in "${search_dirs[@]}"; do
|
||||||
|
desktop_files+=("$dir"/*.desktop)
|
||||||
|
done
|
||||||
|
[ ${#desktop_files[@]} -eq 0 ] && return
|
||||||
|
|
||||||
|
# Single grep pass across all desktop files
|
||||||
|
local -A file_icons file_wmclass
|
||||||
|
local filepath line
|
||||||
|
while IFS=: read -r filepath line; do
|
||||||
|
case "$line" in
|
||||||
|
Icon=*)
|
||||||
|
[ -z "${file_icons[$filepath]:-}" ] && file_icons["$filepath"]="${line#Icon=}"
|
||||||
|
;;
|
||||||
|
StartupWMClass=*)
|
||||||
|
[ -z "${file_wmclass[$filepath]:-}" ] && file_wmclass["$filepath"]="${line#StartupWMClass=}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done < <(grep -H '^Icon=\|^StartupWMClass=' "${desktop_files[@]}" 2>/dev/null)
|
||||||
|
|
||||||
|
# Build class → icon map
|
||||||
|
local icon wm_class bn name
|
||||||
|
for filepath in "${!file_icons[@]}"; do
|
||||||
|
icon="${file_icons[$filepath]}"
|
||||||
|
[ -n "$icon" ] || continue
|
||||||
|
|
||||||
|
wm_class="${file_wmclass[$filepath]:-}"
|
||||||
|
if [ -n "$wm_class" ]; then
|
||||||
|
_WINDOW_ICON_MAP["${wm_class,,}"]="$icon"
|
||||||
|
fi
|
||||||
|
|
||||||
|
bn="${filepath##*/}"
|
||||||
|
name="${bn%.desktop}"
|
||||||
|
_WINDOW_ICON_MAP["${name,,}"]="$icon"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_build_window_icon_map
|
||||||
|
|
||||||
|
icon_for_class() {
|
||||||
|
local class_lower="${1,,}"
|
||||||
|
echo "${_WINDOW_ICON_MAP[$class_lower]:-$class_lower}"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user