LinuxQuestions.org
Download your favorite Linux distribution at LQ ISO.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 10-18-2010, 09:59 AM   #1
Juako
Member
 
Registered: Mar 2010
Posts: 202

Rep: Reputation: 84
Smile Bash script for automatic viewport changing and other WM stuff (works with compiz)


I had to make for work a collectd based network monitoring system which displays data for each server in a fullscreen conky on different compiz viewports. I have to switch viewports automatically when the machine isn't operated (sorta screensaver) and also if i had to call attention in the case something bad happens, autoswitching should stop and the relevant viewport be showed up.

This requires an interface to talk dynamically to the WM, which i've implemented in bash with support of the widely available "wmctrl" program.

Code:
http://paste2.org/p/1177045

Code:
#!/bin/bash

# BASICS ***************************************************************
# enable extended globbing
shopt -qs extglob

# VARS *****************************************************************
scriptName="${0##*/}"
scriptDir=$(readlink -f "${0%/*}")
scriptVersion=1.1
scriptLock="$scriptDir/$scriptName.pid"
scriptPipe="$scriptDir/$scriptName.fifo"

# FUNCS ****************************************************************
getScreenConfig() {
    local wmctrld=$(wmctrl -d | sed 's/\*//g')

    # desktop geometry
    dX=${wmctrld##*DG: } && dX=${dX%%x*}
    dY=${wmctrld#*x} && dY=${dY%% *}

    # viewport geometry
    vX=${wmctrld##*WA: } && vX=${vX#* } && vX=${vX%%x*}
    vY=${wmctrld##*x} && vY=${vY%% *}

    # viewport config
    (( vRows=dX/vX , vCols=dY/vY, vTotal=vRows*vCols ))

    # current viewport
    cvX=${wmctrld##*VP: } && cvY=${cvX#*,}
    cvX=${cvX%%,*} && cvY=${cvY%% *}

    # verbosity
    [[ "$1" == "-v" ]] && {
        echo "Geometry: desktop $dX"x"$dY, viewport: $vX"x"$vY"
        echo "Viewports: $vRows"h \* "$vCols"v = "$vTotal total"
        echo "Current viewport: "$(getV $cvX $cvY)" ($cvX,$cvY)"
    }
}

getX() {
    echo $(( (($1 - 1) % vRows) * vX ))
}

getY() {
    echo $(( (($1 - 1) / vRows) * (vY + 1) ))
}

getV() {
    echo $(( ($1 / vX) + (( $2 / (vY + 1) ) * vRows) + 1 ))
}

gotoViewport() {
    (( $1 > 0 && $1 <= vTotal )) && {
        echo "$FUNCNAME: switching to viewport $1 ("$(getX $1),$(getY $1)")"
        wmctrl -o $(getX $1),$(getY $1)
        return $?
    }
    echo "$FUNCNAME: parameter may be an integer between 1 and $vTotal, got $1"
    return 1
}

gotoWindow() {
    wmctrl -a "$1" && {
        echo "$FUNCNAME: switching to window \"$1\" - viewport "$(getV $cvX $cvY)" ($cvX,$cvY)"
        return $?
    }
    echo "$FUNCNAME: couldn't find a window named \"$1\""
    return 1
}

cmdGoto() {
    ! (( $# )) && echo "$FUNCNAME: need parameters, accepting \$1=viewport|(+|-)viewport|windowname" && return 1
    (( $# > 1 )) && echo "$FUNCNAME: extra parameters, accepting \$1=viewport|(+|-)viewport|windowname" && return 1

    case "$1" in
        # viewport
        +([[:digit:]])) gotoViewport "$1" ;;
        # viewport offset
        @(-|+)+([[:digit:]]))
            getScreenConfig
            local remainder=$(( ($(getV $cvX $cvY) + ($1)) % vTotal ))
            gotoViewport $(( (remainder ? 0 : vTotal) + remainder ))
        ;;
        # window
        *) gotoWindow "$1" ;;
    esac
    return $?
}

cmdRotate() {
    ! (( $# )) && echo "$FUNCNAME: need parameters, accepting \$1=delay \$2=(+|-)step" && return 1
    (( $# > 2 )) && echo "$FUNCNAME: extra parameters, accepting \$1=delay \$2=(+|-)step" && return 1
    [[ "${1//*([[:digit:]])}" ]] && echo "$FUNCNAME: \"$1\" is not a positive integer" && return 1
    [[ "${2//@(-|+)*([[:digit:]])}" ]] && echo "$FUNCNAME: \"$2\" is not a positive step" && return 1

    echo "$FUNCNAME: setting rotation interval to $1 seconds"
    # if there is a rotation loop running, finish it
    [[ "${rotPID+defined}" ]] && cmdStop
    doRotate "$@" &
    rotPID=$!
    return $?
}

cmdStop() {
    [[ ! "${rotPID+defined}" ]] && echo "$FUNCNAME: there isn't any rotation loop running" && return 1
    (($#>1)) && echo "$FUNCNAME: extra parameters, accepting \$1=viewport|(+|-)viewport|nombre-ventana" && return 1

    # if there is a rotation loop running, finish it
    echo "$FUNCNAME: finishing rotation $rotPID"
    kill -15 $rotPID &
    unset rotPID

    # we have a goto
    [[ $1 ]] && cmdGoto "$1"

    return $?
}

doRotate() {
    echo "$FUNCNAME ($BASHPID): starting rotation..."
    trap "trap - TERM && echo $FUNCNAME \($BASHPID\): exiting... && exit 0" TERM
    while sleep $1; do cmdGoto ${2-+1}; done
}

doInit() {
    echo "$FUNCNAME: initializing..."

    # lock
    (set -o noclobber; echo "$$" > "$scriptLock") &>/dev/null || {
        # lock found
        echo "$FUNCNAME: found lockfile, checking if the process is alive..."

        # here goes a check using "cmd pid"

        kill -0 $(cat "$scriptLock") &>/dev/null && \
        echo "$FUNCNAME: found a running proceess with ID " $(cat "$scriptLock")", saliendo..." && exit 0

        # dead pid: clean up
        rm -f $scriptPipe &>/dev/null
        echo "$$" >| "$scriptLock"
    }

    # pipe (connect stdin too)
    mknod "$scriptPipe" p

    # traps
    trap "doExit" INT TERM EXIT
}

doExit() {
    echo "$FUNCNAME: exiting..."

    # if there is a rotation loop running, finish it
    [[ "${rotPID+defined}" ]] && cmdStop

    # delete lock and exit
    rm -fv "$scriptLock" "$scriptPipe"
    exit $?
}

getInput() {
    echo "$scriptName: waiting for commands"
#    while read -ep"-> " line; do
    while true; do
        read line < "$scriptPipe"
        case "$line" in
            @(goto|rotate|stop)*(+([[:space:]])*)) cmd${line^} ;;
            quit) doExit ;;
            pid) echo $$ ;;
            *) echo "$scriptName: unknown command: $line" ;;
        esac
    done
}

# MAIN *****************************************************************
doInit
getScreenConfig -v
getInput
the script can be saved with any name, and i suggest you to run it in the background:
Code:
scriptname &
It opens a pipe, and listens for commands on that pipe, so you can command it with
Code:
echo "command" > pipe
or
Code:
cat>pipe
and start typing away.

Accepted commands:

goto ViewportNumber (1 < ViewportNumber < MaxAvailableViewport)
go to the specified viewport

goto +ViewportOffset
go as many viewports "next", wrapping around as needed (i.e. you can use any positive integer).

goto -ViewportOffset
same as before, but in the "previous" direction

goto WindowName
go to the viewport where WindowName is, and bring it to front. This is simply a direct call to "wmctrl -a" and hence it follows its semantics, i encourage you to read and test it.

rotate Interval
start iterating over all viewports, with a delay of Interval seconds in between

rotate Interval +Step
same as before, but jumping Step viewports on each iteration

rotate Interval -Step
same as before, but in the opposite direction

If there is any rotation already running, rotate will stop it and start a new one with the new parameters.

stop
stop any running rotations. Can accept the same arguments as "goto", which will cause the specified action to take place after stop.

pid
print the script's PID

quit
exit the script and delete the command pipe (can handle soft kill signals sent to it's process, i.e. ctrl-C).

That's it!

This is a work in progresss so i'm asking you to test it and help me to add this features please:
  • first of all: use dbus for comm with compiz
  • run rotations as coprocs
  • command line arguments and send commands to a running process if it detects one
  • better detection of a running instance
  • more commands
  • input via readline
  • ability to handle other WM configs (not only viewports)
Reqs: bash 4, wmctrl, a WM using Viewports (tested only with compiz)

Tell me if you have corrections/code to add/requests/whatever. Enjoy!

Last edited by Juako; 01-04-2011 at 05:17 PM. Reason: code update
 
Old 11-13-2010, 02:47 AM   #2
archtoad6
Senior Member
 
Registered: Oct 2004
Location: Houston, TX (usa)
Distribution: MEPIS, Debian, Knoppix,
Posts: 4,727
Blog Entries: 15

Rep: Reputation: 234Reputation: 234Reputation: 234
Thanks for sharing, it looks useful.

Which WMs besides compiz use Viewports?

Even better than a "Code:" block, you might consider putting a script that long & wide in a pastebin.

Last edited by archtoad6; 11-13-2010 at 02:48 AM. Reason: spelling
 
Old 11-13-2010, 06:03 PM   #3
Juako
Member
 
Registered: Mar 2010
Posts: 202

Original Poster
Rep: Reputation: 84
Glad you find it useful.

Afaik, E16/17 do, i remember doing that kind of panning with it. but there should be more, as

(from man wmctrl)
"wmctrl is a command that can be used to interact with an X Window manager that is compatible with the EWMH/NetWM specification"

maybe other compliant wms do too. it's strange, but in http://en.wikipedia.org/wiki/Compari...indow_managers compiz is listed as NOT compliant, so i don't know.

Anyway, i recently found that more powerful results (with compiz) can be obtained by directly talking to its dbus interface, which allows querying/setting config values, and sending control messages to plugins, etc. I'll soon modify the script to use that interface.

I didn't thought the script was that long, but i'll have the pastebin into account for the next one, thanks!
 
Old 11-14-2010, 04:03 AM   #4
archtoad6
Senior Member
 
Registered: Oct 2004
Location: Houston, TX (usa)
Distribution: MEPIS, Debian, Knoppix,
Posts: 4,727
Blog Entries: 15

Rep: Reputation: 234Reputation: 234Reputation: 234
Don't misunderstand, I said "looks useful", not "is useful". I can't say that because I can't read it, nor do I think I have a WM it will work on. It's actually the width, not the length that is causing me a problem -- the best compromise is Opera at 140%, that gives just over half the screen to the "Code:" block & I can readably see 57 columns.
I wish you would reconsider putting this one in a pastebin.

Finally, don't take the above as lessening my thanks for taking the time to write, test, document, post, share your script.
 
Old 11-14-2010, 06:11 AM   #5
Juako
Member
 
Registered: Mar 2010
Posts: 202

Original Poster
Rep: Reputation: 84
Fair enough! it looks fine on my screen, but then again perhaps if you have a 4:3 or other type it sure can look different.

So sed -i $lastpost "s/I didn't thought the script was that long, but i'll have the pastebin into account for the next one, thanks!/I didn't thought the script was that wide, but i'm having the pastebin into account for this one, thanks!"

Last edited by Juako; 11-14-2010 at 06:14 AM.
 
Old 01-04-2011, 02:52 PM   #6
Juako
Member
 
Registered: Mar 2010
Posts: 202

Original Poster
Rep: Reputation: 84
For the sake of completeness, there's another tool i see mentioned in the compiz forums that could be used for these purposes too:

http://www.semicomplete.com/projects/xdotool

Haven't tried it, though!
 
Old 01-04-2011, 04:40 PM   #7
archtoad6
Senior Member
 
Registered: Oct 2004
Location: Houston, TX (usa)
Distribution: MEPIS, Debian, Knoppix,
Posts: 4,727
Blog Entries: 15

Rep: Reputation: 234Reputation: 234Reputation: 234
You're so right about the 4:3 display -- I did a major re-install & my monitor once again uses its full width & this thread is just in Konqueror.
 
Old 01-04-2011, 05:28 PM   #8
Juako
Member
 
Registered: Mar 2010
Posts: 202

Original Poster
Rep: Reputation: 84
Ah, gotcha! I've update the pastebin too, though my doubts persist on how available it'll be over time. Does LQ have its own area/way to post formatted code? Thought of posting the code in my LQ "blog" and link there... but when posting i realized the editor is the same as here, so same story
 
1 members found this post helpful.
Old 01-05-2011, 06:35 AM   #9
archtoad6
Senior Member
 
Registered: Oct 2004
Location: Houston, TX (usa)
Distribution: MEPIS, Debian, Knoppix,
Posts: 4,727
Blog Entries: 15

Rep: Reputation: 234Reputation: 234Reputation: 234
Juako,

Good post +7.

Thanks for thinking to update the pastebin. I share your doubts about its long tern availability. LQ does not have its own area/way to post formatted code. Feel free to suggest one in LQ S&F, I would support you.

Putting code in one's blog (I've considered that too) at least puts in a separate tab for the reader, but does nothing for using the full width of everyone's screens.

As to an editor, jeremy has an entirely new editor in the works.

Last edited by archtoad6; 01-05-2011 at 07:38 AM.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
bash script automatic file rename if exists AutoC Programming 4 06-23-2015 03:31 PM
emerald+compiz 0.9.0.=segmentation fault.Compiz alone works with no borders. blurred Slackware 1 08-28-2010 11:07 AM
changing filename using Bash Script tmbigrigg Programming 10 11-10-2007 07:02 AM
changing path from a bash script ggeeoo Linux - Newbie 7 01-04-2006 06:35 PM
send automatic input to a script called by another script in bash programming jorgecab Programming 2 04-01-2004 12:20 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 03:32 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration