8

I've created some shortcuts mimicking the Unity's Mod4 + num behavior.

wmctrl -xa Sublime || subl

What I don't like, is that once sublime is running, it always focuses the first opened window. I'd like to focus the last "focused" window. Same as in Unity. Is there a flag for that?

4 Answers 4

15

This script does what you want:

#!/bin/bash

app=$1
workspace=$(wmctrl -d | grep '\*' | cut -d ' ' -f1)
win_list=$(wmctrl -lx | grep $app | grep " $workspace " | awk '{print $1}')

IDs=$(xprop -root|grep "^_NET_CLIENT_LIST_STACKING" | tr "," " ")
IDs=(${IDs##*#})

for (( idx=${#IDs[@]}-1 ; idx>=0 ; idx-- )) ; do
    for i in $win_list; do
        if [ $((i)) = $((IDs[idx])) ]; then
            wmctrl -ia $i
            exit 0
        fi
    done
done

exit 1

EDIT: This script always focuses on the last focused window, as opposed to cycling through the windows in the order they were opened.

EDIT 2: I modified the script (turns out wmctrl and xprop use slightly different formats for displaying hexadecimal numbers).

EDIT 3: app name should be taken from the 3rd column of wmctrl -lx to avoid certain conflicts.

9
  • The question was already answered, providing an extra answer can be helpful but then explain why and what this is a better / different solution.
    – Requist
    Dec 16, 2014 at 20:11
  • 2
    I added an explanation although the first line already says that this script does exactly what the OP asked for. The accepted solution, while useful, does not. Dec 16, 2014 at 20:24
  • Nice script, does exactly what the OP asked for, it is strange the other answer is accepted. Anyway, is it possible to expand this script, so that it could also cycle windows of the same application in chronological focus order?
    – ThisGuy
    Dec 4, 2017 at 17:53
  • Do you mean for the loop to go in the reverse order? I haven't tried it but maybe for (( idx=0 ; idx<${#IDs[@]} ; idx++ )) ; do will do it. Dec 5, 2017 at 15:35
  • No. The current version does not go through loop at all, it just brings the last active window. If there are other open windows of the same app, and if I run the script again, it will not cycle through them. What I am seeking for is to expand this version so that 1) it brings last active window of the app ($1) to foreground, if none of app's windows is currently active, 2) if one of app's windows is already active, bring previously active window of this app, etc.
    – ThisGuy
    Dec 6, 2017 at 4:25
5

I've made a very robust app switcher using wmctrl. Check my Ubuntu Forums post and my askubuntu answer.

This is the script to launch:

#!/bin/bash
app_name=$1
workspace_number=`wmctrl -d | grep '\*' | cut -d' ' -f 1`
win_list=`wmctrl -lx | grep $app_name | grep " $workspace_number " | awk '{print $1}'`


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


# 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
2
  • 1
    So, this doesn't go directly to the last-focused window, but instead lets you iterate through the application's windows in the order in which they were opened. Really nice script....(I'm using it).
    – jonS90
    Apr 9, 2014 at 2:35
  • Exactly. Glad you like it. I've made a whole minimal wmctrl tiling manager (aka a script) and hopefully will publish soon.
    – mreq
    Apr 9, 2014 at 7:50
4

I made a combination of both previous answers which I find quite convenient. It enables to:

  • set the focus to the most recently focused window of the app (answer of Raul Laasner) if that window does not already have the focus;
  • otherwise, cycle to the next window of the app (answer of mreq);
  • if there is no window of the app, open one (there is now an optional second argument which specifies the command to run in that case).

Here is the script:

#!/bin/bash
if [ $# -lt 1 ] ; then
  echo "Usage : $0 <window name> [<command to run if there is no window with that name>]"
  exit 1
fi

app_name=$1

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

# Get the id of the active window (i.e., window which has the focus)
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 the window currently focused matches the first argument, seek the id of the next window in win_list which matches it
if [[ "$win_list" == *"$active_win_id"* ]]; then

    # Get next window to focus on 
    # (sed removes the focused window and the previous windows from the list)
    switch_to=`echo $win_list | sed s/.*$active_win_id// | awk '{print $1}'`

    # If the focused 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 the currently focused window does not match the first argument
else

    # Get the list of all the windows which do
    win_list=$(wmctrl -lx | grep $app_name | awk '{print $1}')

    IDs=$(xprop -root|grep "^_NET_CLIENT_LIST_STACKING" | tr "," " ")
    IDs=(${IDs##*#})

   # For each window in focus order
    for (( idx=${#IDs[@]}-1 ; idx>=0 ; idx-- )) ; do
        for i in $win_list; do

           # If the window matches the first argument, focus on it
            if [ $((i)) = $((IDs[idx])) ]; then
                wmctrl -ia $i
                exit 0
            fi
        done
    done
fi

# If a window to focus on has been found, focus on it
if [[ -n "${switch_to}" ]]
then
    (wmctrl -ia "$switch_to") &

# If there is no window which matches the first argument
else

    # If there is a second argument which specifies a command to run, run it
    if [[ -n "$2" ]]
    then
        ($2) &
    fi
fi

exit 0

Example of usages:

focus_window.sh vlc vlc
focus_window.sh gnome-control-center gnome-control-center
focus_window.sh gnome-terminal gnome-terminal
4
  • 2
    Great, exactly what I was looking for! I think this should be built into gnome so that you can easily assign shortcuts for the most used programs. Note that the cycling is restricted to the current desktop unless there are now matching windows on that desktop - that is perfect imo.
    – dreua
    Dec 23, 2020 at 16:19
  • Unfortunately this doesn't work on Wayland, does anyone have a version which is Wayland compatible? I want exactly the functionality/features of this script, working with it for about three years now, thanks again!
    – dreua
    Sep 21, 2023 at 16:12
  • You can first check if you have wmctrl installed and if the following commands work wmctrl -lx to get the windows id and wmctrl -ia <ID> to focus on the window of id <ID>. Otherwise, you can execute the script in a console and see the output or try each line one by one and see if the output is what is expected.
    – Zach
    Sep 25, 2023 at 8:18
  • I know that but wmctrl does not work with Wayland windows.
    – dreua
    Sep 25, 2023 at 16:18
0

Late for the answer, but managed to do it work gracefully, in my case, using Firefox:

/usr/lib/firefox/firefox && sleep 0.5 && wmctrl -ia $(wmctrl -l Firefox | grep "Mozilla Firefox" | sort -r | head -n 1 | cut -d " " -f 1)

Explanation:

/usr/lib/firefox/firefox - Opens firefox

sleep 0.5 - Gives some time for the window to open

wmctrl -ia - Focus on a specific window ID

$(wmctrl -l Firefox | grep "Mozilla Firefox" | sort -r | head -n 1 | cut -d " " -f 1) - Gets the last window ID, which in my case, is open on the homepage, so its name is "Mozilla Firefox".

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .