Shell – Cycle between open windows with wmctrl

shell-scriptwindow-managementx11

I have a script that I'm trying to use to make handing applications easier. Right now it gets the window id of an application name (the first parameter) and checks if the window_id exists or not. If it doesn't exist, it runs the command to open that applications (second parameter. If it does exist, it uses wmctrl to get the window by window_id and move it to the front.

My plan is to add this script to shortcuts for each application I use often. However, I want to add the ability to cycle through all the windows open for an application, instead of just being able to raise the last one open. Any recommendations on how to do this in bash? Would I need to set a global system variable? Though it's obvious, I'm fairly new to bash. Here's the script for windowctl, the place I want to extend is the get_window_id function.

#!/bin/bash
#command [app_name] [app_command]

function get_window_id() {
    #this is the part I want to extend
    window_id=$(wmctrl -l | grep -i "$1" | tail -1 | cut -f1 -d" ")
}

function open_app() {
    exec $2  
}

get_window_id $1

if [ -z $window_id ]
    then 
        open_app $1 $2
    else
        wmctrl -i -a "$window_id" 
fi

An example would be adding the command windowctl sublime subl3 to Alt+S.

Best Answer

I've been long using a miniscript I named lonew for this. It's meant to be short for "lastof or new". lastof is another script of mine, which attempts to find a visible window that matches a given command a was accessed the most recently.

Both of the scripts are below:

(they might use some refactoring but they get the job done)

lonew:

#!/bin/bash
CMD="$1"; shift; ARGS="$@"
lastof $CMD || { echo $CMD $ARGS; $CMD $ARGS & }
disown

lastof:

#!/usr/bin/env ruby
#open-last
#List all windows and sort them by the time they were last accessed

require 'shellwords'

XTIME="_NET_WM_USER_TIME"

QARGV=ARGV.map {|arg| Shellwords.escape(arg)}

ids=IO.popen "xdotool search --onlyvisible #{QARGV.join(" ")}"
max_time_id=nil
max_time=nil

ids.each_line do |id|
  id.chomp!
  puts "id=#{id}"
  time=`xprop -id #{id} #{XTIME}`.split('=')[1].to_i

  max_time||=time
  max_time_id||=id
  if time > max_time
    max_time=time
    max_time_id=id
  end
end
exit(1) unless max_time_id
puts "Switching to ID: #{max_time_id}"
exit system("xdotool windowactivate #{max_time_id}")

__END__
Related Question