#!/bin/bash
# switch-org-exec - swith to open window or launch new instance of a program
# License: GPLv2+
# Author: Volodymyr M. Lisivka <vlisivka@gmail.com>
set -ue
. import.sh log arguments
WINDOW_CLASS=""
STARTUP_COMMAND=""
# Return ID of active window in hexadecimal form without leading zeroes after 0x (e.g. 0x30000d4 )
activeWindowId() {
xprop -root _NET_ACTIVE_WINDOW | cut -d '#' -f 2 | sed 's/ //g; s/0x0*/0x/; '
}
# Return class of given window (e.g. gedit.Gedit )
windowClass() {
WINDOW_ID="$1"
xprop -id "$WINDOW_ID" WM_CLASS | cut -d '=' -f 2 | sed 's/", "/./; s/[" ]//g;'
}
# Main function
main() {
# Get ID and class of active window
ACTIVE_WINDOW_ID=$( activeWindowId )
ACTIVE_WINDOW_CLASS=$( windowClass "$ACTIVE_WINDOW_ID" )
# If class of active window is not equal to class of target window
if [ "$ACTIVE_WINDOW_CLASS" != "$WINDOW_CLASS" ]
then
# Just switch to first target window of given class (or execute command)
wmctrl -xF -a "$WINDOW_CLASS" || $STARTUP_COMMAND "$@" </dev/null >/dev/null 2>&1 &
else
# Program is already ran and one of it windows is active.
# Try to find more open windows of same class and switch to next available (or do nothing).
# For all windows
wmctrl -lx | (
# Iterate over windows and find ID's of first window and next window of given class
FIRST_WINDOW_ID=''
NEXT_WINDOW_ID='not found'
while read ID DESKTOP CLASS HOST TITLE
do
# Skip windows with different class
[ "$CLASS" == "$WINDOW_CLASS" ] || continue
[ -n "$FIRST_WINDOW_ID" ] || FIRST_WINDOW_ID="$ID"
[ -n "$NEXT_WINDOW_ID" ] || NEXT_WINDOW_ID="$ID"
# If ID of current window is *numerically* equal to active window ID,
# then reset variable NEXT_WINDOW_ID to be set at next iteration
if (( ID == ACTIVE_WINDOW_ID ))
then
NEXT_WINDOW_ID=''
fi
done
# Activate next window or first findow, if next window is not found
if [ "$NEXT_WINDOW_ID" != 'not found' -a -n "$NEXT_WINDOW_ID" ]
then
wmctrl -ia "$NEXT_WINDOW_ID" || :
elif [ -n "$FIRST_WINDOW_ID" ]
then
wmctrl -ia "$FIRST_WINDOW_ID" || :
fi
)
fi
}
parse_arguments "-w|--window-class)WINDOW_CLASS;S" "-c|--command)STARTUP_COMMAND;S" -- "${@}" || exit $?
[ -n "$STARTUP_COMMAND" ] || {
error "Startup command is required for this script."
exit 1
}
# Generate window class, if not filled
[ -n "$WINDOW_CLASS" ] || {
# Strip options, if any
FIRST_WORD_OF_COMMAND="${STARTUP_COMMAND%% *}"
# Generate window class from command. Example:
# COMMAND: gnome-terminal
# WINDOW_CLASS: gnome-terminal.Gnome-terminal
WINDOW_CLASS="$FIRST_WORD_OF_COMMAND.${FIRST_WORD_OF_COMMAND[@]^}"
}
main "${ARGUMENTS[@]:+${ARGUMENTS[@]}}"
exit $?
__END__
=pod
=head1 NAME
switch-or-exec - switch to open window or launch new instance of program
=head1 SYNOPSIS
switch-or-exec [OPTIONS] [-- COMMAND_ARGUMENTS]
=head1 OPTIONS
=over 4
=item B<--help> | B<-h>
Print a brief help message and exit.
=item B<--man>
Show manual page.
=item B<-w> | B<--window-class> WINDOW_CLASS
Window class to switch to.
Hint: use command "wmctrl -lx" to list windows with their classes.
If not given, then command will be used to generate window class
( "cmd" -> "cmd.Cmd" ).
Examples:
gnome-terminal.Gnome-terminal
Navigator.Firefox
=item B<-c> | B<--command> STARTUP_COMMAND
Command to run when window with given class is not found. Options are
allowed.
Required field. Use "/bin/true" for no operation.
=back
Unlike many other programs, this program stops option parsing at first
non-option argument.
Use -- in commandline arguments to strictly separate options and arguments.
All arguments are passed to command, when it is run.
=head1 DESCRIPTION
This script tries to switch to first or next window of already running program,
using window class, or launch new instance of program in background,
when no open windows are found.
Examples:
# Try to switch to window with class "gedit.Gedit"
# or execute command "gedit --encoding=utf-8" in the background
switch-or-exec -c gedit -- --encoding=utf-8
# Try to switch to window with class "Navigator.Firefox"
# or execute command "firefox" in the background
switch-or-exec -w Navigator.Firefox -c firefox
Hint: script set-gnome-shell-custom-binding can be used to assign keybinding from command line.
Examples:
set-gnome-shell-custom-binding -n "Gedit" -a "switch-or-exec -c gedit" -b "<Mod4>g"
set-gnome-shell-custom-binding -n "Firefox" -a "switch-or-exec -w Navigator.Firefox -c firefox" -b "<Mod4>f"
set-gnome-shell-custom-binding -n "gnome-Terminal" -a "switch-or-exec -c gnome-terminal" -b "<Mod4>t"
set-gnome-shell-custom-binding -n "eClipse" -a "switch-or-exec -w Eclipse.Eclipse -c eclipse" -b "<Mod4>c"
set-gnome-shell-custom-binding -n "evolution (Mail)" -a "switch-or-exec -c evolution" -b "<Mod4>m"
set-gnome-shell-custom-binding -n "libre office Writer" -a "switch-or-exec -w VCLSalFrame.libreoffice-writer -c oowrite" -b "<Mod4>w"
set-gnome-shell-custom-binding -n "starDict" -a "switch-or-exec -c stardict" -b "<Mod4>d"
=head1 SEE ALSO
wmctrl(1)
=head1 AUTHOUR
Volodymyr M. Lisivka <vlisivka@gmail.com>
=cut