You are not logged in.
I'm using fluxbox, and I found a script at http://fluxbox-wiki.org/index.php?title … ing_wmctrl that's supposed to:
1. Focus an application if an instance of it is already running
2. Start the application if it's not.
Here's the script reproduced in full:
#!/bin/bash
# Find_app
# Author: Lucas van Staden (lvs at dedmeet.com / www.dedmeet.com)
# This little script will try and find the application attempting to start
# in the running processes, and if found, focus the application
# if not found, a new instance will start
# usage:
# find_app.sh <application with full path>
# params
# 1 - application to start (full path)
# helper applications
WMCTRL=`which wmctrl`;
GREP=`which grep`;
APPLICATION=$1;
BASENAME=`basename $APPLICATION`;
BASENAME=`echo $BASENAME | tr "[:upper:]" "[:lower:]"`
FOUND=0;
function findwindow {
# 1 = BASENAME
# 2 = WMCTRL
# 3 = GREP
IFS=$'\n';
for RUNNING in `$2 -l -x`
do
if [ `echo $RUNNING | tr "[:upper:]" "[:lower:]" | $3 -c $1` -gt 0 ]
then
HOSTNAME=`hostname`
WINDOW=${RUNNING#*${HOSTNAME} }
$2 -a $WINDOW
FOUND=1;
fi;
done
}
findwindow $BASENAME $WMCTRL $GREP;
if [ $FOUND -eq 0 ]
then
$APPLICATION
sleep 2;
findwindow $BASENAME $WMCTRL $GREP;
if [ $FOUND -eq 0 ]
then
sleep 3;
findwindow $BASENAME $WMCTRL $GREP;
fi
fi
I'm a bit confused by it. Specifically, the nested 'if' loops at the end that call the 'findwindow' function three times (why?). So I wrote my own script, borrowing from the first script:
#!/bin/bash
# try to focus on an application if it's already running
# if not, run the application
APP=`basename $1`
FULLAPP="$*"
HOSTNAME=`hostname`
# try to find a running instance of the application and bring it forward
wmctrl -l -x | grep -i $APP | while read RUNNING
do
WINDOW=${RUNNING#*${HOSTNAME} }
wmctrl -a $WINDOW
exit 1
done
# create a new instance of the application if it doesn't already exist
if [ $? -eq 0 ]
then
$FULLAPP
fi
But now I'm wondering if my script could fail in some unexpected way, or if there's any functionality or advantage that the first script has that my script doesn't include.
I know that I don't like the 'exit 1' statement in the middle of the while loop -- I wanted to use a variable other than $? for the test condition in the 'if' statement further down, but was having difficulty.
At any rate, could someone help me either unravel the first script, or improve mine?
Offline
Both scripts pretend to find the window created by an application using the path to its binary, but that's not quite what they do. They merely grep the executable filename (basename $1) from the output of wmctrl -l and use the window title portion of that line to look up the window for the wmctrl -a part.
This may fail if any other program happens to include the programs binary name in their title, e.g. if i opened the website of gqview in firefox first. Running the script for "gqview" will then result in the firefox window being brought up front instead of launching a new gqview instance, since the firefox window title contains the string "GQview" and will thus be mistaken for a gqview window.
A more reliable way can be implemented using xdotool, which provides better means of matching windows. I'd suggest that instead of relying on the window title we match the name portion (first string) of the WM_CLASS property, which is usually equivalent to the binary file name. E.g.:
#!/bin/bash
# try to focus on an application if it's already running
# if not, run the application
if ! xdotool windowactivate "$(xdotool search --name $(basename "$1"))" &> /dev/null; then
$* &
disown
fi
If xdotool's "search" command fails, the "windowactivate" command will fail as well. so in that case we'll launch the arguments to this script as a background job and immediately disown it from bash's job control. The latter assures that the script itself terminates after launching and that the terminal from which we ran it can be closed without closing the app we launched.
This method should be more reliable, although it may still fail, since applications may choose to use a WM_CLASS->name string that differs from it's binary name. But as far as I've tested it, the WM_CLASS->name seems to be equivalent to the binary name (by convention?).
I'm not sure whether there is a reliable way of finding an application window using the binary name of the application. Afaik there's no straightforward way to do so using xprop, xwininfo, wmctrl or xdotool. I may be wrong, though. If anyone knows more, please tell
About the original script: the part after launching the application seems pointless indeed, since that part isn't even reached until the application terminates, and the results of the subsequent findwindow() calls aren't even used either. Looks like some leftover stuff.
Hope this helps
Last edited by hbekel (2009-09-20 09:50:47)
Offline
#!/bin/bash # try to focus on an application if it's already running # if not, run the application if ! xdotool windowactivate "$(xdotool search [b]--name[/b] $(basename "$1"))" &> /dev/null; then $* & disown fi
As written, this script is hit-and-miss for me; it works for songbird and xcalc, but not for firefox and thunderbird. Did you mean to type --class instead of --name, by chance? It appears to work flawlessly after swapping these options.
Thanks a bunch!
Offline
Using --name should work for most apps since the res_name property of an XClassHint should contain the formal name of the application, which is likely to equal the name of the binary (that's the basic assumption the script relies on). It doesn't work for firefox because firefox sets the XClassHint(3) to
WM_CLASS(STRING) = "Navigator", "Firefox"
xdotool --name matches the first string while --class matches the second, which correspond to the res_name and res_class properties of the XClassHint struct.
Using --class works for most other apps (by accident) because they usually set WM_CLASS to something like this:
WM_CLASS(STRING) = "urxvt", "URxvt"
and since xdotools regexp match is case insensitive, "urxvt" will match "URxvt". But this is just being lucky, because the second string (res_class) may be something completely different.
So I'd say first match --name and then match --class as a fallback for cases like firefox:
#!/bin/bash
# try to focus on an application if it's already running
# if not, run the application
wid="$(xdotool search --name --class $(basename "$1") | head -1)"
if [ -n "$wid" ]; then
xdotool windowactivate $wid &> /dev/null
else
$* &
disown
fi
Offline
I'm a bit confused by it. Specifically, the nested 'if' loops at the end that call the 'findwindow' function three times (why?). So I wrote my own script, borrowing from the first script:
Hi,
I am the original creator of the script you found.
Found this post whilst looking for something else.
Firstly, there is an & missing, code should be:
if [ $FOUND -eq 0 ]
then
$APPLICATION &
sleep 2;
findwindow $BASENAME $WMCTRL $GREP;
if [ $FOUND -eq 0 ]
then
sleep 3;
findwindow $BASENAME $WMCTRL $GREP;
fi
fi
and to clarify my madness
I Like using my applications on specific desktops, so Firefox = desktop 2, eclipse = desktop 3 etc etc, and the applications are set to always open on the same desktop.
The loops you see was my attempt to locate the program after it was run, in the event that I was on another desktop when I did my keys to start/locate it.
The second loop was to find any application that took a bit of time to actually open (was an old P4 1.8ghz laptop), so applications did sometimes start slow. (again, a simple workaround to solve the issue at hand)
Not the best way, but it worked, solved the problem at hand.
Hope that clarifies the confusement
This may fail if any other program happens to include the programs binary name in their title
I fully agree, did not notice that when I created the (not the best in the world) code, but I figured it was good enough to share, or at least get the idea out there.
Frankly I am surprised no one with better bashing abilities has not updated the fluxbox wiki already.
hbekel - you should go add your improved script to the flucbox wiki, so others can benefit from it
xdotool - never knew this existed, wmctrl was the best tool I could find to solve my issue.
Offline
I Like using my applications on specific desktops, so Firefox = desktop 2, eclipse = desktop 3 etc etc, and the applications are set to always open on the same desktop.
The loops you see was my attempt to locate the program after it was run, in the event that I was on another desktop when I did my keys to start/locate it.
The second loop was to find any application that took a bit of time to actually open (was an old P4 1.8ghz laptop), so applications did sometimes start slow. (again, a simple workaround to solve the issue at hand)
[...]
Hope that clarifies the confusement
It does indeed, thanks for the feedback. I hadn't thought about that, but I can definitely see why you'd want that. Personally though, I prefer to have the window moved to the current desktop instead, as in "I don't care where it is, I just want it right here".
So I've polished the script a little more to allow both behaviors. By default, this new version will switch to the desktop the window is on, but will move the window to the current desktop if the --move option is supplied:
#!/bin/bash
#
# Show an application window or launch the application if no window
# could be found.
#
# Requires xdotool. (http://www.semicomplete.com/projects/xdotool)
#
# To "show" means "switch to the window's desktop, then raise and
# focus the window."
#
# If the --move option is supplied, the window will be moved to the
# current desktop before raising and focusing it.
#
# The method for finding an existing window that is associated with
# the supplied command relies on the assumption that the basename of
# the first word of the command matches either the "name" or "class"
# portion of the window's WM_CLASS property.
#
# See http://bbs.archlinux.org/viewtopic.php?id=80554 for details.
#
# Author: Henning Bekel <h.bekel at googlemail.com>. Inspired by a
# similar script written by Lucas van Staden <lvs at dedmeet.com /
# www.dedmeet.com>
usage () {
echo "Usage: $(basename $0) [-m|--move] command"
echo
echo " -m, --move : move the window to the current desktop instead of"
echo " switching to the desktop the window is at."
exit 1
}
# show usage if no arguments were given or --help was requested
[[ $# -eq 0 || "$1" =~ ^(-h|--help)$ ]] && usage
move=0
wid=''
# test for --move option
if [[ "$1" =~ ^(-m|--move)$ ]]; then
move=1
shift
fi
mapped? () {
# test wether a window exists whose WM_CLASS name or class
# property matches the binary name supplied. Not using --title
# assures we don't switch to some window which happens to contain
# the binary name as well (i.e. a web browser).
wid="$(xdotool search --name --class $(basename "$1") | head -1)"
[[ "$wid" =~ ^[0-9]+$ ]]
}
show () {
# show the window, that is:
# 1. move the window to the current desktop if requested
# 2. switch to the window's desktop, raise and focus
if [[ move -eq 1 ]]; then
xdotool set_desktop_for_window $wid $(xdotool get_desktop)
fi
xdotool windowactivate $wid &> /dev/null
}
if mapped? "$1"; then
show
else
# launch the command in the background and remove it from jobs
$* &
disown
# wait until the window has been created. We'll exit after 10
# retries to avoid an infinite loop in case the application failed
# to launch at all.
retry=10
until mapped? "$1"; do
sleep 1
((retry-=1))
[[ retry -eq 0 ]] && exit 1
done
# the window may be on a different desktop now, so we'll show it
# again to either move it here (--move) or switch to it's desktop
show
fi
hbekel - you should go add your improved script to the flucbox wiki, so others can benefit from it
I can certainly do that. Should I replace the original script or add it as an alternative? Since both scripts basically do the same, I'd opt for replacing it, if you agree, of course. In any case, it might be a good idea to add a general section that points to both wmctrl and xdotool as means of scripting fluxbox, and to supply this script as an example for using xdotool for this special purpose in a separate section. What do you think?
Edited: now exits if the application failed to launch after 10 seconds, to avoid an infinite loop.
Last edited by hbekel (2009-09-24 12:15:51)
Offline
I think it is best to leave the choice, just in case a user's distro has only one of the applications available to them (Ubuntu has both)
I have adjusted the wiki where the original script is listed ( and updated it a bit to explain the loops )
There is now item 6, and wmctrl is item 6.1.
Your method using xdotool to be 6.2
This leaves it open for future expansion, in case someone comes up with an even better idea....
On another note, I am pretty excited with discovering xdotool, as I think it could solve another issue I have, described here:
http://wiki.autofs.net/archives/message … 17.en.html
Basically been looking for a way to keep my wifes machine from sleeping, as it kills my music, and I think xdotool might just do the trick
Sofar nothing has worked successfully.
Offline
Thanks for the clarifications, dedmeet.
As another alternative, it looks like fluxbox has built-in functionality to do what I was trying to do using the 'If' fluxbox keys command. I haven't had the time to test it, and I don't know whether it uses the name or the class of XClassHint, or something entirely different, but it might be worth looking into.
Figured I'd post that here in case another fluxbox newbie like me comes across this thread.
Offline