Question

I am configuring xbindkeys to change window focus using shortcuts. For example, I managed to create a shortcut to focus on a an application window, let's say a terminator window:

wmctrl -xa terminator

Unfortunately it focuses always at the same terminator window, preventing me to cycle through the terminator windows.

Could you suggest me a command to focus on a terminator window and, if pressed again, will cycle through all the terminator windows, please?

UPDATE 30 Mar 2013

I modified this script http://lars.st0ne.at/blog/switch%20between%20windows%20within%20the%20same%20application to make a script such that

script.sh NAME

focus on application NAME or cycle through all the windows of NAME if a window of it is already focused, but it doesn't work properly.

Here is the script

win_class=$1 # 'terminator' # $1

# get list of all windows matching with the class above
win_list=$(wmctrl -x -l | grep -i $win_class | awk '{print $1}' )

# get id of the focused window
active_win_id=$(xprop -root | grep '^_NET_ACTIVE_W' | awk -F'# 0x' '{print $2}')

# get next window to focus on, removing id active
switch_to=$(echo $win_list | sed s/.*$active_win_id// | awk '{print $1}')

# if the current window is the last in the list ... take the first one
if [ "$switch_to" == '' ];then
   switch_to=$(echo $win_list | awk '{print $1}')
fi

# switch to window
wmctrl -i -a $switch_to

The script does focus on a windows of the application, and cycle through them until it reach a window, I guess the last created. At that point, cycling doesn't work anymore.

Was it helpful?

Solution

I've found a problem in the script, if no window has focus.

May you try the following modified script:

#!/bin/bash
win_class=$1 # 'terminator' # $1

# get list of all windows matching with the class above
win_list=$(wmctrl -x -l | grep -i $win_class | awk '{print $1}' )

# get id of the focused window
active_win_id=$(xprop -root | grep '^_NET_ACTIVE_W' | awk -F'# 0x' '{print $2}')
if [ "$active_win_id" == "0" ]; then
    active_win_id=""
fi

# get next window to focus on, removing id active
switch_to=$(echo $win_list | sed s/.*$active_win_id// | awk '{print $1}')

# if the current window is the last in the list ... take the first one
if [ "$switch_to" == '' ];then
   switch_to=$(echo $win_list | awk '{print $1}')
fi

# switch to window
wmctrl -i -a $switch_to

OTHER TIPS

The script works for me.

Anyway, it seems that the script does not find the active window in you case. Therefore it manages to switch to your application but fails to cycle through. It switches to the fist window in $win_list because, the sed command fails to remove the active window ( and all list entries before ) from $win_list.

Try the the following command:

xprop -root _NET_ACTIVE_WINDOW

The output should be something like this:

_NET_ACTIVE_WINDOW(WINDOW): window id # 0x2400005

The property "_NET_ACTIVE_WINDOW" is part of the EWMH standard. see: http://standards.freedesktop.org/wm-spec/wm-spec-1.3.html

Maybe you are using a non EWMH( Extended Window Manager Hint ) compliant window manager! Which WM are you using?

... some window manager allow to enable EWMH compatibility via configuration or plugin.

After adapting the script by st0ne, I have a version that works generically (don't need to specify the app_name). Hope that is useful to somebody. :)

#!/bin/bash
active_win_id=`xprop -root | grep '^_NET_ACTIVE_W' | awk -F'# 0x' '{print $2}' | awk -F', ' '{print $1}'`
if [ "$active_win_id" == "0" ]; then
    active_win_id=""
fi

app_name=`wmctrl -lx | grep $active_win_id | awk '{print $3}'`
workspace_number=`wmctrl -d | grep '\*' | cut -d' ' -f 1`
win_list=`wmctrl -lx | grep -ri $app_name | grep " $workspace_number " | awk '{print $1}'`

# get next window to focus on, removing id active
switch_to=`echo $win_list | sed s/.*$active_win_id// | awk '{print $1}'`
# if the current window is the last in the list ... take the first one
if [ "$switch_to" == "" ];then
    switch_to=`echo $win_list | awk '{print $1}'`
fi


if [[ -n "${switch_to}" ]]
    then
        (wmctrl -ia "$switch_to") &
    else
        if [[ -n "$2" ]]
            then
                ($2) &
        fi
fi


exit 0

I encountered a small issue1 with tkt028's answer, but I liked what they were doing in terms of handling any generic application. But I also liked how st0ne's answer handles cycling through the windows of a specifically named application. So I combined the approaches.

My script takes an optional first argument to specify an application whose windows should be cycled. If no such windows are found, and if the optional second argument was provided, it falls back to launching the command specified by the second argument.

If no arguments are provided at all, then it just cycles through the windows of the currently active application.

#!/bin/bash

if [[ "$1" == "-h" || "$1" == "--help" ]]; then
    echo "Cycle through windows of the active, or specified, application."
    echo ""
    echo "Usage: $(basename $0) [window_class_name [application_launcher]]"
    echo ""
    echo "  window_class_name:    regex string specifying an application's window name,"
    echo "                        as specified by the third column of"
    echo "                        'wmctrl -l -x'"
    echo "  application_launcher: application to optionally launch if no windows"
    echo "                        matching window_class_name are found"
    echo ""
    echo "If no arguments are specified, cycles through the windows of the active application."
    exit
fi

# get ID of active window
active_win_id=`xprop -root | grep '^_NET_ACTIVE_W' | awk -F'# 0x' '{print $2}' | awk -F', ' '{print $1}'`
if [ "$active_win_id" == "0" ]; then
    active_win_id=""
fi

if [[ -n "$1" ]]; then
    # get app name from input argument
    app_name="$1"
else
    # get corresponding app name
    app_name="${app_name:-$(wmctrl -lx | grep $active_win_id | awk '{print $3}')}"
fi

# get active workspace number
workspace_number=`wmctrl -d | grep '\*' | cut -d' ' -f 1`

# get list of windows corresponding to the desired app
win_list=`wmctrl -lx | grep -i $app_name | grep " $workspace_number " | awk '{print $1}'`

# get next window of app to focus on
#
# (Parses $win_list as a single string, removing everything except the token
# after the active ID. If active ID is sole token or last token, string will be
# left unmodified, producing an array from which we'll extract the first element.)
# Note: If active window was not of class app_name, then this will end up
#       selecting the first window of app_name, if running. Otherwise, we'll fall
#       through to launching a new instance of the app in the else of the next block.
switch_to=($(echo $win_list | sed "s/.*\<\(0x0\+\)\?$active_win_id\>\s*\(\<0x[0-9a-f]\+\>\).*/\2/"))

# if we have a valid window to switch to, do so
if [[ -n "${switch_to}" ]]; then
    wmctrl -ia "${switch_to[0]}"
    exit $?
else
    # if the user specified a fallback application to run if target window
    # was not found, try to launch it
    if [[ -n "$2" ]]; then
        $2 &
        # check whether process corresponding to PID of background
        # process we just launched is still running
        ps -p $! > /dev/null
        exit $?
    else
        exit $?
    fi
fi

1 The recursive grep on this line in tkt028's answer didn't work in my environment. Maybe it's dependent on your version of grep.

win_list=`wmctrl -lx | grep -ri $app_name | grep " $workspace_number " | awk '{print $1}'`

I simply removed the r argument from the grep, and then their script worked as advertised.

win_list=`wmctrl -lx | grep -i $app_name | grep " $workspace_number " | awk '{print $1}'`
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top