Last active
December 15, 2024 21:01
-
-
Save tkapias/0443d4930c1d7b520f4496e1ff391625 to your computer and use it in GitHub Desktop.
Toggle or launch a new instance of a window with i3wm on a single screen setup.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# Example for i3wm exec binding: toggle or run urxvtc in client mode (systemd daemon urxvtd), with setenv to bypass TMUX in my bashrc. | |
# bindcode $mod+Shift+49 exec --no-startup-id "/home/user/.config/i3/scripts/wtoggle.sh -i -c \\"^URxvt$\\" -n \\"^Terminal\sURxvt$\\" -m \\"urxvtc -title 'Terminal URxvt' -e sh -c 'TMUX=false bash'\\" -s \\"urxvtd\\"" | |
# locale | |
export LC_ALL="C.UTF-8" | |
export TZ=:/etc/localtime | |
Help() | |
{ | |
cat <<- 'HEREDOC' | |
This script is useful for i3wm. | |
Currently for single screen setup only. | |
- If it matches a window, it will bring it to active/focus from any position or hide/close it. | |
- If there is no match it will launch a new command/service, or both, in daemon/client mode. | |
Syntax: ./wtoggle.sh [-h] [Options...] | |
options: | |
-h Print this Help. | |
-c "<winClassPattern>" Regex pattern for the matching window classname. | |
-n "<winNamePattern>" Regex pattern for the matching window name or title. | |
-m "<Cmd>" Command to launch if there is no match. | |
-s "<Service>" Systemd service to check & launch in priority if there is no match. | |
-i Use i3wm to send the active window to the scratchpad if it match. | |
Default: gracefully close the window with bonk. | |
-v Look only for visible windows for match, works well for sticky windows. | |
Default: lookup at all windows, hoping that its order is right. | |
Mandatory for matching: -c AND/OR -n | |
Mandatory for running: -m AND/OR -s | |
HEREDOC | |
} | |
requirements=( xdotool bonk ) | |
for cmd in "${requirements[@]}"; do | |
if [[ -z $(command -v $cmd) ]]; then | |
cat <<- "HEREDOC" | |
Command $cmd could not be found. | |
Requirements: | |
- xdotool: sudo apt xdotool | |
- bonk: https://github.com/FascinatedBox/bonk' | |
HEREDOC | |
exit 1 | |
fi | |
done | |
# default to lookup for any windows | |
_visibleMode="--all" | |
while getopts ":hc:n:m:s:iv" option; do | |
case $option in | |
h ) Help; exit 0 ;; | |
c ) _winClassPattern="${OPTARG}" ;; | |
n ) _winNamePattern="${OPTARG}" ;; | |
m ) _Cmd="${OPTARG}" ;; | |
s ) _Service="${OPTARG}" ;; | |
i ) _i3Mode=true ;; | |
v ) _visibleMode="" ;; | |
\?) echo -e "Unknown option: -$OPTARG \n" >&2; Help; exit 1;; | |
: ) echo -e "Missing argument for -$OPTARG \n" >&2; Help; exit 1;; | |
* ) echo -e "Unimplemented option: -$option \n" >&2; Help; exit 1;; | |
esac | |
done | |
# Mandatory | |
if [[ -z $_winClassPattern ]] && [[ -z $_winNamePattern ]]; then | |
echo -e "Error: option -c or -n is mandatory.\n" >&2; Help; exit 1 | |
fi | |
if [[ -z $_Cmd ]] && [[ -z $_Service ]]; then | |
echo -e "Error: option -m and/or -s are mandatory.\n" >&2; Help; exit 1 | |
fi | |
# help to lower the risk of concurrencies | |
renice -n 10 $$ | |
# ID focused windows | |
_winActiveId=$(bonk get-active 2>/dev/null) | |
# List matching window and select last | |
sleep 0.1 | |
if [[ -z $_winClassPattern ]]; then | |
_winNameIds=$(bonk select $_visibleMode --title "${_winNamePattern}" 2>/dev/null) | |
_winMatchId=$(echo "${_winNameIds}" | tail -1) | |
elif [[ -z $_winNamePattern ]]; then | |
_winClassIds=$(bonk select $_visibleMode --classname "${_winClassPattern}" 2>/dev/null) | |
_winMatchId=$(echo "${_winClassIds}" | tail -1) | |
else | |
_winNameIds=$(bonk select $_visibleMode --title "${_winNamePattern}" 2>/dev/null) | |
_winClassIds=$(bonk select $_visibleMode --classname "${_winClassPattern}" 2>/dev/null) | |
_winMatchIds=$(echo -e "${_winNameIds}\n${_winClassIds}") | |
for _id in $_winMatchIds; do | |
count=$(echo "$_winMatchIds" | grep -c "$_id") | |
[[ ! "$count" == "1" ]] && _winMatchId+=$(echo -e "\n$_id") | |
done | |
_winMatchId=$(echo "$_winMatchId" | tail -1) | |
fi | |
# if match is focused | |
if [[ "${_winActiveId:-0}" == "${_winMatchId:-1}" ]]; then | |
if [[ -n $_i3Mode ]]; then | |
i3-msg -q [id="${_winMatchId}"] move scratchpad | |
else | |
bonk close -w "${_winMatchId}" 2>/dev/null | |
fi | |
# a match exist | |
elif [[ -n $_winMatchId ]]; then | |
xdotool set_desktop_for_window "${_winMatchId}" "$(xdotool get_desktop)" 2>/dev/null | |
bonk activate -w "${_winMatchId}" 2>/dev/null | |
sleep 0.1 | |
if [[ ! "${_winMatchId}" == "$(bonk get-active 2>/dev/null)" ]]; then | |
eval "${_Cmd}" | |
fi | |
# no match and there is a service to check | |
elif [[ -n $_Service ]]; then | |
if ! systemctl --user is-active --quiet "${_Service}"; then | |
systemctl --user start "${_Service}" 2>/dev/null | |
fi | |
if [[ -n $_Cmd ]]; then | |
sleep 1 | |
eval "${_Cmd}" | |
fi | |
# no match and no service to check | |
else | |
eval "${_Cmd}" | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I just discovered a new xdotool alternative called bonk.
As I am still experiencing some concurrency issues sometime with xdotool, I updated the script with bonk to try if for some time.
The old version using xdotool and wmctrl is in the third revision.