Using FZF as a Universal Launcher on Sway
Every picker, launcher, and selector on my desktop is just a list piped into FZF inside a floating terminal window. No rofi, no dmenu, no wofi. Just FZF.
The idea is simple: if you can generate a list, you can make a launcher. The Sway window manager makes this trivial with for_window rules that turn any terminal into a centered floating picker.
The Application Launcher
This one-liner replaces a traditional application launcher entirely:
bindsym $mod+d exec foot --app-id='launcher' -- bash -c 'compgen -c | sort -u | fzf | xargs -r swaymsg -t command exec;'
for_window [app_id="^launcher$"] floating enable, border none, resize set width 50 ppt height 50 ppt, move position centercompgen -c lists every executable on your $PATH. Pipe it through sort -u to deduplicate, let FZF do the fuzzy matching, and xargs -r swaymsg -t command exec tells Sway to launch whatever you picked. The for_window rule makes it float, removes the border, sizes it to 50% of the screen, and centers it.
That is the entire application launcher. No config files, no plugins, no daemon running in the background.
The Emoji Picker
Same pattern, different list. The emoji picker reads from a CSV file and pipes it through FZF:
bindsym $mod+colon exec $USER_BIN/emoji-picker/emoji-picker.sh
for_window [app_id="^emoji-picker$"] floating enable, border none, resize set width 65 ppt height 60 ppt, move position centerThe script itself processes a CSV of emojis with awk, presents them in FZF with a preview pane showing the emoji large, and copies the selection to clipboard:
emoji_data=$(awk -F ', ' '{gsub(/"/, "", $2); print $1 "\t" $2}' "$EMOJI_FILE")
selected=$(echo -e "$emoji_data" | fzf --reverse --border rounded \
--prompt "Find emoji: " \
--preview "echo -e {1}" \
--preview-window up:1 \
--header "Select an emoji (press ESC to cancel)" \
--info inline)
if [ -n "$selected" ]; then
emoji=$(echo "$selected" | awk '{print $1}')
echo -n "$emoji" | wl-copy
notify-send "Emoji copied to clipboard" "$emoji"
fiThe CSV is just emoji, "description" pairs. Adding new emojis is editing a text file.
The Monitor Profile Switcher
I switch between an LG UltraGear ultrawide, an XREAL One Pro, HDMI displays, and the laptop's internal screen. Each has different resolution, refresh rate, and scaling needs. An FZF picker lets me switch between them with $mod+m:
bindsym $mod+m exec foot --app-id='monitor-profile' -- "$HOME/dotfiles/sway/profiles/fzf-monitor-profile.sh" &
for_window [app_id="^monitor-profile$"] floating enable, border none, resize set width 60 ppt height 40 ppt, move position centerThe script defines profiles as a simple bash array with descriptions:
profiles=(
"Auto-detect:Automatically detect and apply appropriate profile"
"LG UltraGear:3440x1440@160Hz, internal display disabled"
"XREAL One Pro:1920x1080@120Hz 1.5x scale, internal display disabled"
"HDMI Only:External HDMI 1920x1080@60Hz, internal display disabled"
"Dual Default:External monitor on top, laptop screen centered below"
"Internal Only:Use only laptop display"
)
selected=$(printf '%s\n' "${profiles[@]}" | fzf --prompt="Select monitor profile: " --height=40% --reverse)The selected profile name maps to a function in a shared monitors.conf that calls the appropriate swaymsg output commands. The auto-detect option runs a setup script that reads connected monitors via swaymsg -t get_outputs and picks the right profile by model name.
The Pattern
Every one of these follows the same three-step recipe:
- Launch
foot(or any terminal) with a unique--app-id - Pipe a list into
fzfand do something with the result - Add a
for_windowrule in your Sway config to float and size the window
My reading list manager ($mod+o), and my tmux session chooser ($mod+Shift+Return). Every new picker is just another script and two lines in the Sway config.
The best part is that FZF's fuzzy matching is consistently excellent across all of them. Learn one interface, use it everywhere.
