You are not logged in.

#276 2012-11-21 16:59:06

ninjaaron
Member
Registered: 2010-12-10
Posts: 296

Re: herbstluftwm

Just to bring it back to hlwm:
While you're definitely right in general, there is little chance that this will be a problem for anyone using herbstluftwm. The default autostart and panel.sh scripts require bash builtins, as do most of the example scripts.

Offline

#277 2012-11-21 22:14:23

mentat
Member
From: France
Registered: 2009-01-13
Posts: 138
Website

Re: herbstluftwm

You've got the point.
Most herbstluftwm users are Arch users so they have last bash version.
Funny, today i just update to the new bash version  4.2.39(2)-release.

It's a great feature to have wm wich can be drive by high level bash scripts.
As users we can easly (or worstly) build the wm that fit our needs.
It's the reason I switch from ratpoison to herbstluftwm and didn't go to Stumpwm (Lisp was to exotic for my lazy imperative programming training brain).

Offline

#278 2012-11-24 19:04:53

wretch
Member
From: pdx
Registered: 2012-11-24
Posts: 22

Re: herbstluftwm

Great wm. Moved from xmonad and hlwm is dozens easier to configure.

I got a couple of questions though.
1. set_layout isn't working for me. At all. Calling it from a shell, keybind or in autostart, it doesn't do anything to the current frame. Cycle_layout works just fine for me though. set_layout returns zero and doesn't print anything to stdout when I call it? Or am I just misinterpretting what this command does?
2. Being an xmonad migrant, -cycle makes more sense to me than -focus. Having four directions is useful for me only sometimes; I usually have only 2 or three windows open on one tag at a time. So 'alt-tab'ing once or twice is usually quicker for me than thinking which direction a window is. Anyways, cycle and cycle_all work just fine for focusing what I want, but what about shifting windows? Like moving the focused window in the same direction/order as cycle does? I may have missed something in the man pages, but I can't think of a way to do it from a script.
3. Is there a way to query if a window is the last window in a direction? For example, I'm using a dual head machine: I have focus on the tag on the left monitor, and I call shift right. If the window is already all the way to the right, I want to move it to the tag on the other monitor. Since (I think) hlwm doesnt return anything if you try to shift a window, but it's already the last in the direction, is there a way to query this?
4. As per #3. Would it be possible to return a non-zero when you call shift like in the situation above?

Anyways, Im really enjoying hlwm so far. Keep up the good work, Thorsten. And thanks everyone for sharing your scripts, they've helped me figure out how to configure my own. If I come up with any super clever scripts I'll make sure to share.

Offline

#279 2012-11-24 21:13:11

The Compiler
Member
From: Switzerland
Registered: 2011-05-01
Posts: 214
Website

Re: herbstluftwm

Hey wretch,

just to not confuse you, I'm not thorsten, but I also started developing on hlwm some days ago.

wretch wrote:

1. set_layout isn't working for me. At all. Calling it from a shell, keybind or in autostart, it doesn't do anything to the current frame. Cycle_layout works just fine for me though. set_layout returns zero and doesn't print anything to stdout when I call it? Or am I just misinterpretting what this command does?

Odd. Are you sure you're calling it right? If you open 3 windows, and do "herbstclient set_layout grid" for example, nothing happens?
Can you try to update to the -git version from the AUR? I implemented error messages there.

2. Being an xmonad migrant, -cycle makes more sense to me than -focus. Having four directions is useful for me only sometimes; I usually have only 2 or three windows open on one tag at a time. So 'alt-tab'ing once or twice is usually quicker for me than thinking which direction a window is. Anyways, cycle and cycle_all work just fine for focusing what I want, but what about shifting windows? Like moving the focused window in the same direction/order as cycle does? I may have missed something in the man pages, but I can't think of a way to do it from a script.

You _could_ probably do that in a script with parsing dump-output, but it'd be a pain. I added that to my (endless) todo list.

3. Is there a way to query if a window is the last window in a direction? For example, I'm using a dual head machine: I have focus on the tag on the left monitor, and I call shift right. If the window is already all the way to the right, I want to move it to the tag on the other monitor. Since (I think) hlwm doesnt return anything if you try to shift a window, but it's already the last in the direction, is there a way to query this?
4. As per #3. Would it be possible to return a non-zero when you call shift like in the situation above?

In the -git version it does:

[florian@ginny ~]$ herbstclient shift right
[florian@ginny ~]$ echo $?
0
[florian@ginny ~]$ herbstclient shift right
shift: No neighbour found
[florian@ginny ~]$ echo $?
6

If you don't want to install the git-version, this will be released as 0.5 as soon as I or thorsten (or someone else) implemented another major feature, monitor names.

Florian


>>> from __future__ import braces
  File "<stdin>", line 1
SyntaxError: not a chance

Offline

#280 2012-11-26 20:15:09

wretch
Member
From: pdx
Registered: 2012-11-24
Posts: 22

Re: herbstluftwm

Hey Florian, thanks for the reply. Good to hear someone else is on board for development.

The Compiler wrote:

Odd. Are you sure you're calling it right? If you open 3 windows, and do "herbstclient set_layout grid" for example, nothing happens?
Can you try to update to the -git version from the AUR? I implemented error messages there.

Ahhh, it was a syntax error. I thought the arguement for set_layout was an index (0=vertical,1=horizontal,etc). Okay, cool, I got it working.
I personally think picking by index would be useful. I'll take a look at the source and see if its something I can contribute.

The Compiler wrote:

In the -git version it does:

[florian@ginny ~]$ herbstclient shift right
[florian@ginny ~]$ echo $?
0
[florian@ginny ~]$ herbstclient shift right
shift: No neighbour found
[florian@ginny ~]$ echo $?
6

Yeah. I upgraded to git and this works perfectly for my use, thanks.

If anyone is curious, this is my script that replaces shift&focus left&right. This is for dual head, and when you call it and there are no neighbors, it moves to the opposite monitor:

#! /bin/bash

# $1 is either shift or focus (default)
# $2 is direction ( + is right (default), - is left )

function hc () {
    herbstclient "$@"
}

function post_shift () {
    # Shifts window to first or last place 
    #    in the event of a monitor swap
    
    case $1 in
        left)
            ps_dir=right
           ;;
        right)
            ps_dir=left
           ;;
    esac

    hc shift $ps_dir
    ps_return=$?

    while [ $ps_return == 0 ]; do
        hc shift $ps_dir
        ps_return=$?
    done
}

# Parse arguements
case $1 in
    s*)
        SF=shift
        ;;
    *)
        SF=focus
        ;;
esac

if [ "$2" == "-" ] || [ "$2" == "l" ]; then
    LR=left
else
    LR=right
fi

# Try to move in direction
hc $SF $LR

# Catch error, then do something
if [ $? == 6 ]; then
    case $SF in
        shift)
            # Find tag on opposite monitor and move window to it
            tags=$(hc tag_status)
            for tag in $tags; do
                if echo $tag | grep '-' > /dev/null; then
                    target=`echo $tag | sed s/-//`
                fi
            done 
            hc move $target

            # Focus the window we just moved
            hc cycle_monitor
            hc focus_nth -1

            # Make sure focused window is all the way at edge
            post_shift $LR

            ;;
        focus)
            hc cycle_monitor
            case $LR in
                right)
                    hc focus_nth 0
                    ;;
                left)
                    hc focus_nth -1
                    ;;
            esac
            ;;
    esac
fi

I haven't tested it with too many frame and window layouts, and its a bit kludgy when you shift right to the next monitor, but its a start.


Also, I've been thinking about if there would be benefits to herbstclient being able to read from stdin. When you have a long sequence of calls to hc with no computation in between, each call must start a new instance. I'm curious at what point opening a pipe and piping a sequence of commands to a single hc instance would be quicker than sucessive calls to hc. I just took a look at the source for hc and it shouldn't be too hard to impliment, so I think I'm gunna do it, do some speed tests and get back to you guys in a few days.
Just seeing if anyone has input before I take a crack at it. Should I write a seperate executable or add functions to hc (what makes more philosophical sense)? Does the time the ipc takes make the time of the setup of hc and the time of new process creation insignificant for any reasonable amount of commands? Is setting up a pipe just to relay to the ipc buffer just plain silly?

Offline

#281 2012-11-26 21:16:28

ninjaaron
Member
Registered: 2010-12-10
Posts: 296

Re: herbstluftwm

wretch wrote:

Also, I've been thinking about if there would be benefits to herbstclient being able to read from stdin. When you have a long sequence of calls to hc with no computation in between, each call must start a new instance. I'm curious at what point opening a pipe and piping a sequence of commands to a single hc instance would be quicker than sucessive calls to hc. I just took a look at the source for hc and it shouldn't be too hard to impliment, so I think I'm gunna do it, do some speed tests and get back to you guys in a few days.
Just seeing if anyone has input before I take a crack at it. Should I write a seperate executable or add functions to hc (what makes more philosophical sense)? Does the time the ipc takes make the time of the setup of hc and the time of new process creation insignificant for any reasonable amount of commands? Is setting up a pipe just to relay to the ipc buffer just plain silly?

I'm interested in this idea... though now that I think of it, I think the "chain" command might allow you to do this already. Not sure what goes on inside the chain command, so it still be separate instances under the hood, but I don't know why it would be.

You're script looks great, by the way. Might try to implement it over here.

[edit]
reimplemented my dynamic tiling script using the chain command rather separate instances of herbstclient (plus other ongoing tweaks):

#!/bin/bash

## SWAP! ##
# functions to give herbstluftwm some dynamic tiling behaviors.
# by Aaron "ninjaaron" Christianson <ninjaaron@gmail.com>

# Usage #
### auto
#    Swaps client in and out of "master" or "stack." If there is only one frame,
#    "auto" splits it. Example keybinding in herbstluftwm's autostart:
#        hc keybind $Mod-semicolon spawn /PATH/TO/swap auto
### stack_spawn
#    focuses stack and runs command. good wrapper for terminal:
#        hc keybind $Mod-period spawn /PATH/TO/swap stack_spawn xterm
### master_spawn
#    moves clients in master to stack and runs command. possible wrapper for
#    dmenu_run:
#        hc keybind $Mod-comma spawn /PATH/TO/swap master_spawn dmenu_run
### close
#    If client in master frame is closed, it is replaced by a client from the
#    stack. If stack is empty, it's removed. used like hlwm "close" command:
#        hc keybind $Mod-slash spawn /PATH/TO/swap close

# CONFIG #
# split_direction = frame split's orientation. "horizontal" or "vertical"
# split_ratio determines split's location. value between 0.1 and 0.9
# stack_layout = stack's hlwm layout. "horizontal" "vertical" "max" "grid"
# stack_frame = frame for stack. 0=top/left 1=bottom/right d=smaller frame
split_direction="horizontal"
split_ratio="0.6667" 
stack_layout="vertical" 
stack_frame=d 
# Here ends the section for users #

hc() { herbstclient $@; }
set_vars() { ### some vars need to be set more than once.
  dump=( $(hc dump) ) # put the content of hc dump into an array
  # values based directly on the dump
  Ratio=${dump[1]#*.} # make the division of primary frame an int
  Orientation=${dump[1]%%:*} # frames split horizontal or vertical
  Frame=${dump[1]##*:} # frame with active client 
  Empty0=${dump[3]: -1} # empty frames =')'; same for following 2 vars
  Empty1=${dump[${#dump[@]}-1]##*:};Empty1=${Empty1:1:1}
  EmptyAll=${dump[1]#*:};EmptyAll=${EmptyAll:1:1}
  # use absolute values from dump to determine values relative to stack/master.
  if [[ $stack_frame = "d" || "$split_direction" != "$Orientation" ]]
  then [ ${Ratio:0:1} -ge 5 ] && stack=1 || stack=0
  else  stack="${stack_frame:-1}"
  fi
  [ $Frame = "0" ] && emptyInact=$Empty1 || emptyInact=$Empty0
  if [[ $Orientation = "horizontal" && $stack = "1" ]]; then
    stackD=r; masterD=l; emptyStack="$Empty1"; emptyMaster="$Empty0"
  elif [[ $Orientation = "vertical" && $stack = "1" ]]; then
    stackD=d; masterD=u; emptyStack="$Empty1"; emptyMaster="$Empty0"
  elif [[ $Orientation = "horizontal" && $stack = "0" ]]; then
    stackD=l; masterD=r; emptyStack="$Empty0"; emptyMaster="$Empty1"
  elif [[ $Orientation = "vertical" && $stack = "0" ]]; then
    stackD=u; masterD=d; emptyStack="$Empty0"; emptyMaster="$Empty1"
  fi
  if [ $Frame = $stack ]
  then origin=$stackD; target=$masterD # $origin = active frame
  else origin=$masterD; target=$stackD # $target = inactive frame
  fi
}; set_vars # do all that stuff I just said

### functions called by the luser
auto() { ### swap out of the current frame into the other.
  if [ -n "$EmptyAll" ]; then # EmptyAll is null if 1 frame with clients exists.
    cmds="shift -e $target" # move client to target frame.
    [ "$emptyInact" != ')' ] && 
    cmds="$cmds , cycle -1 , shift -e $origin , focus -e $target , cycle 1"
  else # one frame? split it. move client to new frame, set stack layout.
    hc split $split_direction $split_ratio; set_vars
    [ "$split_direction" = "horizontal" ]&&cmds="shift -e r"||cmds="shift -e d"
    cmds="$cmds , focus -e $stackD , set_layout $stack_layout "
    [ $stack = 0 ] && hc cmds="$cmds , focus -e $masterD"
  fi; hc chain , "$cmds"
}
stack_spawn() { ### spawn applications in the stack.
  [ "$EmptyAll" = ')' ] && $@ || # just spawn if no other clients exist.
    if [ -z "$EmptyAll" ]; then $@; auto; else hc focus -e $stackD; $@; fi
}
master_spawn() { ### spawn applications in the master.
  if [ "$EmptyAll" =  ')' ]
  then $@
  else # moves all the clients in master out and then spawn
    [ -z "$EmptyAll" ] && auto
    while [ "$emptyMaster" != ')' ]; do
      hc chain , focus -e $masterD , shift -e $stackD , focus -e $masterD
      set_vars
    done;"$@"
  fi
}
close() { ### replace master client on close. removes stack if empty.
  hc close; set_vars
  [ $emptyMaster = ')' ]&&
    hc chain , focus -e $stackD , shift -e $masterD&&set_vars
  [ $emptyStack = ')' ] && hc chain , focus -e $stackD , remove
}
$@;exit # do something and exit

Last edited by ninjaaron (2012-11-27 06:29:37)

Offline

#282 2012-11-26 23:28:44

wretch
Member
From: pdx
Registered: 2012-11-24
Posts: 22

Re: herbstluftwm

ninjaaron wrote:

I'm interested in this idea... though now that I think of it, I think the "chain" command might allow you to do this already. Not sure what goes on inside the chain command, so it still be separate instances under the hood, but I don't know why it would be.

Ahhh, the chain command! It looks like that's the way to go. From here, it looks like hc passes all arguments to hlwm, which does the parsing of the chained commands. So that solves the speed thing.

As far as redirection goes, you can just use substitution instead of a stdin pipe, so I guess something like this is not all that worthwhile to work on. Reading commands seperated by a newline from a pipe instead the chain command is just sugar. I'll probably end up doing ot anyway when I'm bored sometime in the near future. Imho nothing wrong with sugar smile.

Thanks for the tip, btw. I've totally skimmed by the chain command every read of the man.

ninjaaron wrote:

You're script looks great, by the way. Might try to implement it over here.

Thanks. Swapping from left to right monitor is a little ugly, but I can't think of a better way to do it. When I move a window to a different tag it ends up on the far right of the last frame, so there is quite a bit of flicker doing shift left in that while loop. Any ideas?

Your scipt is looking great as always. I may or may not have stolen a lot of the ideas you've shared smile.

Offline

#283 2012-11-27 07:00:19

ninjaaron
Member
Registered: 2010-12-10
Posts: 296

Re: herbstluftwm

No problem! It's flattering to think that a "script kiddie" like me have anything to contribute to the work of "real programmers."

As far as shifting LTR without as much flickering, you could probably cut it down a bit if you used "hc shift -e $ps_dir"; that would shift to the next frame to the $ps_dir, so it wouldn't be as bad, but there would still be flickering.

The final solution to all of this nonsense (from a scripting perspective) is to write something in a language that can fully parse `herbstclient dump`. From there, you could use `herbstclient load` to get any client exactly where you want it.

I've been looking for a reason to learn python, and this might be it.

PS where feasible, I've reimplemented all of my `hc chain` commands in "swap" with variable assignments that are simply executed in one long chain command at the end. I can no longer hit my keybinding for `auto` quickly enough in succession to make it glitch. Mission accomplished.

Might see if I can do the same with my autostart file to make my settings load instantly like in dwm or xmonad.
[edit] yup, this is possible with autostart, after a little smashing of the head against the wall.

talk about a dirty hack:

#!/bin/bash

#I have a ton of functions for hlwm, mostly that I want available for 
# keybindings. They are all in a single file that just executes one of
# them at the end
chmod +x ~/.config/herbstluftwm/func ~/bin/* ~/.themes/xresources-gen
conf="$HOME/.config/herbstluftwm/"
func="$conf"func

function hc() { herbstclient "$@"; }

# remove all existing keybindings
cmds="keyunbind --all"

cmds="$cmds , emit_hook reload"

cmds="$cmds , set default_frame_layout 3"

# keybindings
Mod=Mod4
cmds="$cmds , keybind $Mod-Shift-q quit"
cmds="$cmds , keybind $Mod-Shift-r reload"
cmds="$cmds , keybind $Mod-Shift-c close"
cmds="$cmds , keybind $Mod-x close"
cmds="$cmds , keybind $Mod-Return spawn urxvtc"
cmds="$cmds , keybind $Mod-period spawn $conf/swap stack_spawn urxvtc"
cmds="$cmds , keybind Shift-F3 spawn $HOME/bin/light dub"
cmds="$cmds , keybind Shift-F2 spawn $HOME/bin/light half"
cmds="$cmds , keybind XF86AudioRaiseVolume spawn pamixer --increase 5"
cmds="$cmds , keybind XF86AudioLowerVolume spawn pamixer --decrease 5"
cmds="$cmds , keybind XF86AudioMute spawn pamixer --toggle-mute"

# tags
TAG_NAMES=( {1..9} )
TAG_KEYS=( {1..9} 0 )

cmds="$cmds , rename default ${TAG_NAMES[0]} || true"
for i in ${!TAG_NAMES[@]} ; do
  cmds="$cmds , add ${TAG_NAMES[$i]}"
  key="${TAG_KEYS[$i]}"
  if ! [ -z "$key" ] ; then
    cmds="$cmds , keybind $Mod-$key use_index $i"
    cmds="$cmds , keybind $Mod-Shift-$key move_index $i"
  fi
done

# layouting
cmds="$cmds , keybind $Mod-r remove"
cmds="$cmds , keybind $Mod-space cycle_layout 1"
cmds="$cmds , keybind $Mod-u split vertical 0.5"
cmds="$cmds , keybind $Mod-o split horizontal 0.5"
for i in {1..9}; do
  cmds="$cmds , keybind $Mod-Alt-$i split horizontal 0.$i"
  cmds="$cmds , keybind $Mod-Control-$i split vertical 0.$i"
done
cmds="$cmds , keybind $Mod-0 spawn $func autogap "
cmds="$cmds , keybind $Mod-s floating toggle"
cmds="$cmds , keybind $Mod-f fullscreen toggle"
cmds="$cmds , keybind $Mod-p pseudotile toggle"
# switch to different layouts directly
cmds="$cmds , keybind $Mod-Alt-v set_layout vertical"
cmds="$cmds , keybind $Mod-Alt-h set_layout horizontal"
cmds="$cmds , keybind $Mod-Alt-m set_layout max"
cmds="$cmds , keybind $Mod-Alt-g set_layout grid"
# "Master Window" type stuff
cmds="$cmds , keybind $Mod-semicolon spawn $conf/swap auto "
cmds="$cmds , keybind $Mod-slash spawn $conf/swap close"

# resizing
RESIZESTEP=0.01
cmds="$cmds , keybind $Mod-Control-h resize left +$RESIZESTEP"
cmds="$cmds , keybind $Mod-Control-j resize down +$RESIZESTEP"
cmds="$cmds , keybind $Mod-Control-k resize up +$RESIZESTEP"
cmds="$cmds , keybind $Mod-Control-l resize right +$RESIZESTEP"

# mouse
cmds="$cmds , mouseunbind --all"
cmds="$cmds , mousebind $Mod-Button1 move"
cmds="$cmds , mousebind $Mod-Alt-Button1 resize"
cmds="$cmds , mousebind $Mod-Button3 zoom"

# focus
cmds="$cmds , keybind $Mod-BackSpace   cycle_monitor"
cmds="$cmds , keybind $Mod-Tab         cycle_all +1"
cmds="$cmds , keybind $Mod-Shift-Tab   cycle_all -1"
cmds="$cmds , keybind $Mod-c cycle"
cmds="$cmds , keybind $Mod-h focus left"
cmds="$cmds , keybind $Mod-j focus down"
cmds="$cmds , keybind $Mod-k focus up"
cmds="$cmds , keybind $Mod-l focus right"
cmds="$cmds , keybind $Mod-i jumpto urgent"
cmds="$cmds , keybind $Mod-Shift-h shift left"
cmds="$cmds , keybind $Mod-Shift-j shift down"
cmds="$cmds , keybind $Mod-Shift-k shift up"
cmds="$cmds , keybind $Mod-Shift-l shift right"
cmds="$cmds , keybind $Mod-m toggle focus_follows_mouse"

# rules
cmds="$cmds , unrule -F"
cmds="$cmds , rule focus=on"
cmds="$cmds , rule windowtype~'_NET_WM_WINDOW_TYPE_(SPLASH)' pseudotile=on"
cmds="$cmds , rule windowtype~'_NET_WM_WINDOW_TYPE_(NOTIFICATION|DOCK|DESKTOP)' manage=off"

# unlock, just to be sure
cmds="$cmds , unlock"

cmds="$cmds , set tree_style '╾│ ├└╼─┐'"

# do multi monitor setup here, e.g.:

# defaults
cmds="$cmds , set focus_follows_shift 1"
cmds="$cmds , set raise_on_focus 1"
cmds="$cmds , set always_show_frame 0"
cmds="$cmds , set focus_follows_mouse 1"
cmds="$cmds , set_layout grid"
cmds="$cmds , pad 0 12 1 1 1"
hc chain , $cmds

"$func" retheme
"$func" gap $(cat "$conf"gap) # remembers the window_gap from the previous session

# find the panel
panel=~/.config/herbstluftwm/panel.sh
[ -x "$panel" ] || panel=/etc/xdg/herbstluftwm/panel.sh
for monitor in $(herbstclient list_monitors | cut -d: -f1) ; do
  # start it on each monitor
  $panel $monitor &
done
exit

Last edited by ninjaaron (2012-11-27 09:35:05)

Offline

#284 2012-11-27 17:36:03

wretch
Member
From: pdx
Registered: 2012-11-24
Posts: 22

Re: herbstluftwm

ninjaaron wrote:

As far as shifting LTR without as much flickering, you could probably cut it down a bit if you used "hc shift -e $ps_dir"; that would shift to the next frame to the $ps_dir, so it wouldn't be as bad, but there would still be flickering.

The final solution to all of this nonsense (from a scripting perspective) is to write something in a language that can fully parse `herbstclient dump`. From there, you could use `herbstclient load` to get any client exactly where you want it.

Hmm. I think I missed the hints about hc load in my first reading of this thread, its real use didn't really click upon first read. I'll look into it, sounds like I might be able to work something out.

ninjaaron wrote:

I've been looking for a reason to learn python, and this might be it.

Do it! Seeing all the "dirty hacks" you do in bash, I think the learning curve you would have is pretty small. I bet if you'd just learn the (very straight forward) syntax, and you had a reference for built-ins (google), you'd be off in no time. 

ninjaaron wrote:

Might see if I can do the same with my autostart file to make my settings load instantly like in dwm or xmonad.
[edit] yup, this is possible with autostart, after a little smashing of the head against the wall.

talk about a dirty hack:

#!/bin/bash

#I have a ton of functions for hlwm, mostly that I want available for 
# keybindings. They are all in a single file that just executes one of
# them at the end
chmod +x ~/.config/herbstluftwm/func ~/bin/* ~/.themes/xresources-gen
conf="$HOME/.config/herbstluftwm/"
func="$conf"func

function hc() { herbstclient "$@"; }

# remove all existing keybindings
cmds="keyunbind --all"

cmds="$cmds , emit_hook reload"

cmds="$cmds , set default_frame_layout 3"

# keybindings
Mod=Mod4
cmds="$cmds , keybind $Mod-Shift-q quit"
cmds="$cmds , keybind $Mod-Shift-r reload"
cmds="$cmds , keybind $Mod-Shift-c close"
cmds="$cmds , keybind $Mod-x close"
cmds="$cmds , keybind $Mod-Return spawn urxvtc"
cmds="$cmds , keybind $Mod-period spawn $conf/swap stack_spawn urxvtc"
cmds="$cmds , keybind Shift-F3 spawn $HOME/bin/light dub"
cmds="$cmds , keybind Shift-F2 spawn $HOME/bin/light half"
cmds="$cmds , keybind XF86AudioRaiseVolume spawn pamixer --increase 5"
cmds="$cmds , keybind XF86AudioLowerVolume spawn pamixer --decrease 5"
cmds="$cmds , keybind XF86AudioMute spawn pamixer --toggle-mute"

# tags
TAG_NAMES=( {1..9} )
TAG_KEYS=( {1..9} 0 )

cmds="$cmds , rename default ${TAG_NAMES[0]} || true"
for i in ${!TAG_NAMES[@]} ; do
  cmds="$cmds , add ${TAG_NAMES[$i]}"
  key="${TAG_KEYS[$i]}"
  if ! [ -z "$key" ] ; then
    cmds="$cmds , keybind $Mod-$key use_index $i"
    cmds="$cmds , keybind $Mod-Shift-$key move_index $i"
  fi
done

# layouting
cmds="$cmds , keybind $Mod-r remove"
cmds="$cmds , keybind $Mod-space cycle_layout 1"
cmds="$cmds , keybind $Mod-u split vertical 0.5"
cmds="$cmds , keybind $Mod-o split horizontal 0.5"
for i in {1..9}; do
  cmds="$cmds , keybind $Mod-Alt-$i split horizontal 0.$i"
  cmds="$cmds , keybind $Mod-Control-$i split vertical 0.$i"
done
cmds="$cmds , keybind $Mod-0 spawn $func autogap "
cmds="$cmds , keybind $Mod-s floating toggle"
cmds="$cmds , keybind $Mod-f fullscreen toggle"
cmds="$cmds , keybind $Mod-p pseudotile toggle"
# switch to different layouts directly
cmds="$cmds , keybind $Mod-Alt-v set_layout vertical"
cmds="$cmds , keybind $Mod-Alt-h set_layout horizontal"
cmds="$cmds , keybind $Mod-Alt-m set_layout max"
cmds="$cmds , keybind $Mod-Alt-g set_layout grid"
# "Master Window" type stuff
cmds="$cmds , keybind $Mod-semicolon spawn $conf/swap auto "
cmds="$cmds , keybind $Mod-slash spawn $conf/swap close"

# resizing
RESIZESTEP=0.01
cmds="$cmds , keybind $Mod-Control-h resize left +$RESIZESTEP"
cmds="$cmds , keybind $Mod-Control-j resize down +$RESIZESTEP"
cmds="$cmds , keybind $Mod-Control-k resize up +$RESIZESTEP"
cmds="$cmds , keybind $Mod-Control-l resize right +$RESIZESTEP"

# mouse
cmds="$cmds , mouseunbind --all"
cmds="$cmds , mousebind $Mod-Button1 move"
cmds="$cmds , mousebind $Mod-Alt-Button1 resize"
cmds="$cmds , mousebind $Mod-Button3 zoom"

# focus
cmds="$cmds , keybind $Mod-BackSpace   cycle_monitor"
cmds="$cmds , keybind $Mod-Tab         cycle_all +1"
cmds="$cmds , keybind $Mod-Shift-Tab   cycle_all -1"
cmds="$cmds , keybind $Mod-c cycle"
cmds="$cmds , keybind $Mod-h focus left"
cmds="$cmds , keybind $Mod-j focus down"
cmds="$cmds , keybind $Mod-k focus up"
cmds="$cmds , keybind $Mod-l focus right"
cmds="$cmds , keybind $Mod-i jumpto urgent"
cmds="$cmds , keybind $Mod-Shift-h shift left"
cmds="$cmds , keybind $Mod-Shift-j shift down"
cmds="$cmds , keybind $Mod-Shift-k shift up"
cmds="$cmds , keybind $Mod-Shift-l shift right"
cmds="$cmds , keybind $Mod-m toggle focus_follows_mouse"

# rules
cmds="$cmds , unrule -F"
cmds="$cmds , rule focus=on"
cmds="$cmds , rule windowtype~'_NET_WM_WINDOW_TYPE_(SPLASH)' pseudotile=on"
cmds="$cmds , rule windowtype~'_NET_WM_WINDOW_TYPE_(NOTIFICATION|DOCK|DESKTOP)' manage=off"

# unlock, just to be sure
cmds="$cmds , unlock"

cmds="$cmds , set tree_style '╾│ ├└╼─┐'"

# do multi monitor setup here, e.g.:

# defaults
cmds="$cmds , set focus_follows_shift 1"
cmds="$cmds , set raise_on_focus 1"
cmds="$cmds , set always_show_frame 0"
cmds="$cmds , set focus_follows_mouse 1"
cmds="$cmds , set_layout grid"
cmds="$cmds , pad 0 12 1 1 1"
hc chain , $cmds

"$func" retheme
"$func" gap $(cat "$conf"gap) # remembers the window_gap from the previous session

# find the panel
panel=~/.config/herbstluftwm/panel.sh
[ -x "$panel" ] || panel=/etc/xdg/herbstluftwm/panel.sh
for monitor in $(herbstclient list_monitors | cut -d: -f1) ; do
  # start it on each monitor
  $panel $monitor &
done
exit

This is brilliant. Good work. I implimented this on my side. Yes, this is executes much quicker. As expected.

However, you don't need to make all those changes to make it work like this.

I just changed the function for hc, initiated $cmds as an empty string, then executed herbstclient $chain at the end:

#! /bin/bash

function hc() {
    cmds="$cmds , $@"
}

cmds=""

hc blah blah blah
hc blah blah blah
....

herbstclient chain , $cmds

[edit] edited because the script was broken.[/edit]
I commented out my original hc function, so doing it this makes it easy to switch back in forth. Just in case, I don't know, chain breaks in an update. And the syntax is nicer imo. Thanks for the hint, as always.

Last edited by wretch (2012-11-28 02:56:38)

Offline

#285 2012-11-27 18:19:23

ninjaaron
Member
Registered: 2010-12-10
Posts: 296

Re: herbstluftwm

wretch wrote:
ninjaaron wrote:

The final solution to all of this nonsense (from a scripting perspective) is to write something in a language that can fully parse `herbstclient dump`. From there, you could use `herbstclient load` to get any client exactly where you want it.

Hmm. I think I missed the hints about hc load in my first reading of this thread, its real use didn't really click upon first read. I'll look into it, sounds like I might be able to work something out.

I can't promise this will work exactly like I've envisioned it, cause I haven't tried to do this with the `load` command before. So maybe we could get one of the devs to comment before going through all the craziness this would entail.


This is brilliant. Good work. I implimented this on my side. Yes, this is executes much quicker. As expected.

However, you don't need to make all those changes to make it work like this.

I just changed the function for hc, initiated $cmds as an empty string, then executed herbstclient $chain at the end:

#! /bin/bash

function hc() {
    cmds="$cmds , $1"
}

cmds=""

hc blah blah blah
hc blah blah blah
....

herbstclient $cmds

I commented out my original hc function, so doing it this makes it easy to switch back in forth. Just in case, I don't know, chain breaks in an update. And the syntax is nicer imo. Thanks for the hint, as always.

Good stuff. It didn't take so long to do it my way, 'cause I'm a vi regex ninja, but your approach is obviously much better for other reason too. variables > constants, as always.

Offline

#286 2012-11-27 18:53:59

wretch
Member
From: pdx
Registered: 2012-11-24
Posts: 22

Re: herbstluftwm

wretch wrote:
#! /bin/bash

function hc() {
    cmds="$cmds , $@"
}

cmds=""

hc blah blah blah
hc blah blah blah
....

herbstclient chain , $cmds

@ninjaaron I totally didn't test this fully. Seems not to work on reboot, some things are probably broken on reload too. Whoops.

EDIT:

Oh, that $1 should be $@. Whoops

#! /bin/bash

function hc() {
    cmds="$cmds , $@"
}

cmds=""

hc blah blah blah
hc blah blah blah
....

herbstclient chain , $cmds

[edit] edited because the script was broken.

Last edited by wretch (2012-11-28 02:55:52)

Offline

#287 2012-11-27 20:27:18

ninjaaron
Member
Registered: 2010-12-10
Posts: 296

Re: herbstluftwm

Yeah, I did it with $@, just to be safe. I was sort of amazed that $1 was working for you. 8^D (oh, and you forgot the "chain" command on the way out too)

[edit]
Decided that this is a useful enough tweak to add it to the original post.

Last edited by ninjaaron (2012-11-27 22:36:13)

Offline

#288 2012-11-28 02:46:55

wretch
Member
From: pdx
Registered: 2012-11-24
Posts: 22

Re: herbstluftwm

ninjaaron wrote:

Yeah, I did it with $@, just to be safe. I was sort of amazed that $1 was working for you. 8^D (oh, and you forgot the "chain" command on the way out too)

smile As for that second comment, maybe I should just stick to copy and paste.

ninjaaron wrote:

[edit]
Decided that this is a useful enough tweak to add it to the original post.

Awesome, I hope it helps some people.


I wrote a new command for hlwm, which does server-side what my script above did client-side. It shifts the focused window all the way to the edge of the tag. Use: herbstclient shift_edge [left/right/up/down]. Its a little dirty, but I'm sure it would be useful to some people, its much quicker server-side. The diff is here, patch away:
[edit]ps I'm running the latest git version, hlwm -v says 4.1.

diff -rupN herbstluftwmoriginal/src/layout.c herbstluftwm/src/layout.c
--- herbstluftwmoriginal/src/layout.c	2012-11-27 18:21:29.862454000 -0800
+++ herbstluftwm/src/layout.c	2012-11-27 18:10:41.192558000 -0800
@@ -1767,3 +1767,18 @@ void frame_update_border(Window window,
     }
 }
 
+int frame_move_window_edge(int argc, char** argv, GString* output) {
+    // Moves a window to an edge in the specified direction
+    int check=0;
+
+    while ( check == 0 ){
+    check=frame_move_window_command(argc,argv,output);
+    }
+
+    g_string_append_printf(output,"...complete");
+
+    return 0;
+
+}
+
+
diff -rupN herbstluftwmoriginal/src/layout.h herbstluftwm/src/layout.h
--- herbstluftwmoriginal/src/layout.h	2012-11-27 18:21:29.862454000 -0800
+++ herbstluftwm/src/layout.h	2012-11-27 17:58:07.826553000 -0800
@@ -163,6 +163,7 @@ bool frame_focus_window(HSFrame* frame,
 bool focus_window(Window win, bool switch_tag, bool switch_monitor);
 // moves a window to an other frame
 int frame_move_window_command(int argc, char** argv, GString* output);
+int frame_move_window_edge(int argc, char** argv, GString* output);
 /// removes the current frame
 int frame_remove_command(int argc, char** argv);
 int close_or_remove_command(int argc, char** argv);
diff -rupN herbstluftwmoriginal/src/main.c herbstluftwm/src/main.c
--- herbstluftwmoriginal/src/main.c	2012-11-27 18:21:29.862454000 -0800
+++ herbstluftwm/src/main.c	2012-11-27 17:56:07.647191000 -0800
@@ -111,6 +111,7 @@ CommandBinding g_commands[] = {
     CMD_BIND(             "resize",         frame_change_fraction_command),
     CMD_BIND(             "focus",          frame_focus_command),
     CMD_BIND(             "shift",          frame_move_window_command),
+    CMD_BIND(             "shift_edge",     frame_move_window_edge),
     CMD_BIND_NO_OUTPUT(   "remove",         frame_remove_command),
     CMD_BIND(             "set",            settings_set_command),
     CMD_BIND(             "toggle",         settings_toggle),

I used this in my old script posted above along with the chain ninjaaron suggested, and while the script is still sluggish, it works better now. Also, again, I haven't tested with too many layouts, I run a fairly simple setup, so use at your own risk?

#! /bin/bash

# $1 is either shift or focus (default)
# $2 is direction ( + is right (default), - is left )

function hc () {
    herbstclient "$@"
}


# Parse arguements
case $1 in
    s*)
        SF=shift
        ;;
    *)
        SF=focus
        ;;
esac

if [ "$2" == "-" ] || [ "$2" == "l" ]; then
    LR=left
else
    LR=right
fi

# Try to move in direction
hc $SF $LR

# Catch error, then do something
if [ $? == 6 ]; then
    case $SF in
        shift)
            # Find tag on opposite monitor and move window to it
            tags=$(hc tag_status)
            for tag in $tags; do
                if echo $tag | grep '-' > /dev/null; then
                    target=`echo $tag | sed s/-//`
                fi
            done 

            # Focus the window we just moved
            hc chain , move $target , cycle_monitor , focus_nth -1

            # Make sure focused window is all the way at edge
            if [ $LR == "right" ]; then
                hc shift_edge left 
            else
                hc shift_edge right
            fi


            ;;
        focus)
            hc cycle_monitor
            case $LR in
                right)
                    hc focus_nth 0
                    ;;
                left)
                    hc focus_nth -1
                    ;;
            esac
            ;;
    esac
fi

Last edited by wretch (2012-11-28 03:03:33)

Offline

#289 2012-11-28 08:04:49

The Compiler
Member
From: Switzerland
Registered: 2011-05-01
Posts: 214
Website

Re: herbstluftwm

wretch wrote:

I wrote a new command for hlwm, which does server-side what my script above did client-side. It shifts the focused window all the way to the edge of the tag. Use: herbstclient shift_edge [left/right/up/down]. Its a little dirty, but I'm sure it would be useful to some people, its much quicker server-side. The diff is here, patch away:

Awesome! Would you mind cleaning it up a bit (indent correctly in the while loop, change spaces so they match the rest of the code [in the while-line and around =]) and then sending it to herbstluftwm-devel@lists.sourceforge.net? Then thorsten can include it in the main repo if he wants to.

Flo


>>> from __future__ import braces
  File "<stdin>", line 1
SyntaxError: not a chance

Offline

#290 2012-11-28 10:25:04

wretch
Member
From: pdx
Registered: 2012-11-24
Posts: 22

Re: herbstluftwm

The Compiler wrote:

Awesome! Would you mind cleaning it up a bit (indent correctly in the while loop, change spaces so they match the rest of the code [in the while-line and around =]) and then sending it to herbstluftwm-devel@lists.sourceforge.net? Then thorsten can include it in the main repo if he wants to.

Thanks! And awesome. Question: I've written a few newer commands as well. Should I create diffs from the clone for each? Or one patch? I've tracked the changes I've made in a man-made file, would that be easier? Sorry, I'm new at this smile



So besides shift_edge, I've also written two more. One is the negate command. This inverts the return status of the following command (if the command returns a 0, ! returns a one, vice versa for non-zeros), useful in and chains and such.

$ hc focus right
focus: No neighbour found
$ echo $?
6
$ hc ! focus right
focus: No neighbour found
$ echo $?
0
$ 

The other one is a cycle_monitor command, but it shifts the focused window to the tag on the other monitor instead of cycling focus.

hc cycle_monitor [delta] (default is +1)

Here is the the diff for these three (hlwm-git, v4.1):

diff -rupN herbclean/src/command.c herbstluftwm/src/command.c
--- herbclean/src/command.c	2012-11-28 01:49:44.743185862 -0800
+++ herbstluftwm/src/command.c	2012-11-28 01:54:24.875012555 -0800
@@ -576,3 +576,11 @@ int command_chain_command(int argc, char
     return command_chain(separator, condition, argc, argv, output);
 }
 
+int invert_return(int argc, char** argv, GString* output) {
+    if (argc <= 1) {
+        return HERBST_NEED_MORE_ARGS;
+    }
+    (void)SHIFT(argc, argv);
+    int status = call_command(argc, argv, output);
+    return (!status);
+}
diff -rupN herbclean/src/command.h herbstluftwm/src/command.h
--- herbclean/src/command.h	2012-11-28 01:49:44.739852546 -0800
+++ herbstluftwm/src/command.h	2012-11-28 01:54:54.038188855 -0800
@@ -58,5 +58,7 @@ int command_chain(char* separator, bool
 
 int command_chain_command(int argc, char** argv, GString* output);
 
+int invert_return(int argc, char** argv, GString* output);
+
 #endif
 
diff -rupN herbclean/src/layout.c herbstluftwm/src/layout.c
--- herbclean/src/layout.c	2012-11-28 01:49:44.736519231 -0800
+++ herbstluftwm/src/layout.c	2012-11-28 01:53:23.848673310 -0800
@@ -1767,3 +1767,12 @@ void frame_update_border(Window window,
     }
 }
 
+int frame_move_window_edge(int argc, char** argv, GString* output) {
+    // Moves a window to an edge in the specified direction
+    int check=0;
+    while (check == 0){
+        check=frame_move_window_command(argc,argv,output);
+    }
+    g_string_append_printf(output,"...complete");
+    return 0;
+}
diff -rupN herbclean/src/layout.h herbstluftwm/src/layout.h
--- herbclean/src/layout.h	2012-11-28 01:49:44.736519231 -0800
+++ herbstluftwm/src/layout.h	2012-11-28 01:53:48.898539486 -0800
@@ -168,6 +168,7 @@ int frame_remove_command(int argc, char*
 int close_or_remove_command(int argc, char** argv);
 void frame_set_visible(HSFrame* frame, bool visible);
 void frame_update_border(Window window, unsigned long color);
+int frame_move_window_edge(int argc, char** argv, GString* output);
 
 #endif
 
diff -rupN herbclean/src/main.c herbstluftwm/src/main.c
--- herbclean/src/main.c	2012-11-28 01:49:44.739852546 -0800
+++ herbstluftwm/src/main.c	2012-11-28 01:52:46.125542904 -0800
@@ -157,6 +157,9 @@ CommandBinding g_commands[] = {
     CMD_BIND(             "getenv",         getenv_command),
     CMD_BIND(             "setenv",         setenv_command),
     CMD_BIND(             "unsetenv",       unsetenv_command),
+    CMD_BIND(             "shift_edge",     frame_move_window_edge),
+    CMD_BIND(             "!",              invert_return),
+    CMD_BIND(             "cycle_shift",    cycle_shift),
     {{ NULL }}
 };
 
diff -rupN herbclean/src/monitor.c herbstluftwm/src/monitor.c
--- herbclean/src/monitor.c	2012-11-28 01:49:44.739852546 -0800
+++ herbstluftwm/src/monitor.c	2012-11-28 01:55:25.854684001 -0800
@@ -1024,3 +1024,16 @@ void monitor_restack(HSMonitor* monitor)
     g_free(buf);
 }
 
+int cycle_shift(int argc, char** argv, GString* output) {
+    int index;
+    if (argc <= 1) {  
+        index = 1;
+    } else {
+        index = atoi(argv[1]);
+    }
+    int i = ( g_cur_monitor + index ) % g_monitors->len;
+    HSMonitor* monitor = monitor_with_index(i);
+    tag_move_focused_client(monitor->tag);
+ 
+    return 0;
+}
diff -rupN herbclean/src/monitor.h herbstluftwm/src/monitor.h
--- herbclean/src/monitor.h	2012-11-28 01:49:44.739852546 -0800
+++ herbstluftwm/src/monitor.h	2012-11-28 01:55:50.034553704 -0800
@@ -93,5 +93,7 @@ bool detect_monitors_xinerama(XRectangle
 bool detect_monitors_simple(XRectangle** ret_rects, size_t* ret_count);
 int detect_monitors_command(int argc, char **argv, GString* output);
 
+int cycle_shift(int argc, char** argv, GString* output);
+
 #endif
 

Devs, I'd love some critiques, I haven't done any c programming in forever, let alone contributing (never), so my syntax is sloppy and my code is unelegant.



Here is my script using these new commands. I mostly wrote these commands so I could write this script with only one call to hc smile

#! /bin/bash

# $1 is either shift or focus (default)
# $2 is direction ( + is right (default), - is left )

function hc () {
    herbstclient "$@"
}

case $1 in
    s*)
        if [ $2 == "+" ]; then
            hc and , ! shift right , cycle_shift , cycle_monitor , focus_nth -1 , shift_edge left 
        else
            hc and , ! shift left , cycle_shift , cycle_monitor , focus_nth -1 
        fi
    ;;
    *)
        case $2 in
            "-")
                hc and , ! focus left , cycle_monitor , focus_nth -1
                    ;;
            *)
                hc and , ! focus right , cycle_monitor , focus_nth 0
                    ;;
        esac
    ;;
esac

If you missed it earlier in the thread, its a replacement for shift&focus left&right, so that they move across monitors. The and command with the negation makes it super snappy compared to older iterations.

Offline

#291 2012-11-28 15:23:05

The Compiler
Member
From: Switzerland
Registered: 2011-05-01
Posts: 214
Website

Re: herbstluftwm

wretch wrote:

Question: I've written a few newer commands as well. Should I create diffs from the clone for each? Or one patch? I've tracked the changes I've made in a man-made file, would that be easier? Sorry, I'm new at this smile

I guess you aren't familiar with git? This would provide a much nicer workflow. I usually do something like this (assuming you're in a freshly cloned git repo):

$ git checkout -b feature1 # create new git branch
... do some work on feature 1 ...
$ git commit -a # commit changes
... do some more work on feature 1 ...
$ git commit -a # commit new changes

$ git checkout master # switch back to original version
$ git checkout -b feature2 # create branch for feature 2
... do some work on feature 2 ...
$ git commit -a # commit changes
... do some more work on feature 2 ...
$ git commit -a # commit new changes

$ git format-patch feature1...master # get patches for feature 1
$ git format-patch feature2...master # get patches for feature 2
... send one mail with a patchset for each feature...

For your case, I guess it would be fine to create one patch per feature against master (however way you like to) and then send one mail per feature to the mailinglist.

Devs, I'd love some critiques, I haven't done any c programming in forever, let alone contributing (never), so my syntax is sloppy and my code is unelegant.

On the mailinglist you'll also get feedback, I'm fairly new to C as well.

You can subscribe on the Mailman page.


>>> from __future__ import braces
  File "<stdin>", line 1
SyntaxError: not a chance

Offline

#292 2012-11-29 20:36:33

Wintervenom
Member
Registered: 2008-08-20
Posts: 1,011

Re: herbstluftwm

I'm not sure if this has already been suggested, but I think it would be quite useful if there was a spawn consequence to run something if a rule is matched.  This would make it possible to, for example, automatically set up, layout, and jump to a tag for certain applications, as well as all sorts of other convenient fanciness.

Last edited by Wintervenom (2012-11-29 20:36:43)

Offline

#293 2012-12-02 04:49:54

wretch
Member
From: pdx
Registered: 2012-11-24
Posts: 22

Re: herbstluftwm

Hey guys. As an excercise to remind myself where scripting isn't necessary, I set out rewrite ninjaaron's swap script using only hlwm's and, or, and chain commands and only a single call to herbstclient. (Not trying to one up you, aaron smile )

After messing with the source a little and an all-day headache, what I ended up with is an autostart that automagically generates this string and substitutes it in for the call to keybind. Meaning the "script" lives in memory with hlwm, and no new processes are started when you press the bound keys. This is advantageous with shorter scripts, although for longer ones it may not be better, do to the nature of constructing a logical decision tree leading to dead ends. In this case, I haven't done any speed tests, but my version of the script is wayyy less elegant than aarons.

I did this by writing functions in a script for each keybind which simply parse the user settings and echo the resulting logical string for that command. Then I source this script in autostart and substitute.

Anyways, this is the script I came up with. As it says in the comments, it requires the query patch in my github.

EDIT: I've updated the script, it should be slightly easier to follow now. Details are a couple posts down.

#! /bin/bash

#########################
######### SWAP! #########
#########################
# functions to give herbstluftwm some dynamic tiling behaviors.
# originaly by Aaron "ninjaaron" Christianson <ninjaaron@gmail.com>
# rewritten by Tylo Hart (Wretch) <http://github.com/htylo>

# The generate_command commands echo single strings utilizing the logical operators and
#    chains that can be executed with a single call to herbstclient

# This script currently requires two patches, the negation patch and the query patch,
#    both available at <http://github.com/htylo/hlwm_patches>


#########################
######### USAGE #########
#########################
# For general usage, source this scipt, then substitute the output of a generate command as
#    input for herbstclient

### Functions:
## auto
#    Swaps client in and out of "master" or "stack." If there is only one frame,
#    "auto" splits it. Example keybinding in herbstluftwm's autostart:
#        source /path/to/this/script ; hc $(generate_auto)
## stack_spawn
#    focuses stack and runs command. good wrapper for terminal:
#        source /path/to/this/script ; hc $(generate_stack_spawn)
## master_spawn
#    moves clients in master to stack and runs command. possible wrapper for
#    dmenu_run:
#        source /path/to/this/script ; hc $(generate_master_spawn)
## close
#    If client in master frame is closed, it is replaced by a client from the
#    stack. If stack is empty, it's removed. used like hlwm "close" command:
#        hc spawn /path/to/this/script close

# Note: The "close" command does not work as a chain, because a chain exits one close is called.
#            So to use the close command, spawn this script with argument "close"


# For keybindings, source this file and bind the output of the generate_command functions to a key in autostart.

### ~/.config/herbstluftwm/autostart
# source /path/to/this/scipt
# hc keybind $Mod-a $(generate_auto)
# hc keybind $Mod-d $(generate_stack_spawn [command] )
# hc keybind $Mod-f $(generate_master_spawn [command] )
# hc keybind $Mod-r spawn /path/to/this/scipt close

#########################
##### USER SETTINGS #####
#########################

# The split direction of stack and master
split_direction="horizontal"
# The ratio of master to stack on the screen
split_ratio="0.6667" 
# The layout of the stack
stack_layout="vertical" 
# The direction of the stack relative the master
stack_dir=right

# hc command to return if a frame is empty
query_frame_empty="cquery 0 nwindows"
# hc command to return if there's only 1 frame on a tag
query_frame_n="cquery 1 nframe"
# hc command to return if there is only 1 window on a frame
query_window_alone="cquery 1 nwindows"


#########################
###### Logic Helper #####
#########################

source /home/marsh/bin/herb/scripts/herbstlogic

#########################
## PARSE USER SETTINGS ##
#########################

# Assure stack_sir actually coincides with split_direction.
if [ $split_direction == horizontal ] && (( $stack_dir != "right" || $stack_dir != "left" ))
then 
    echo '$split_direction and $stack_dir mismatch. Defaulting to stack_dir=right'
    stack_dir=right
elif [ $split_direction == vertical ] && (( $stack_dir != "up" || $stack_dir != "down" ))
then 
    echo '$split_direction and $stack_dir mismatch. Defaulting to stack_dir=down'
    stack_dir=down
fi

# Get the direction of the master relative the stack
case $stack_dir in
    r*) master_dir=left
        focus_toggle=$(herbstlogic "focus -e l | focus -e r")
        shift_toggle=$(herbstlogic "shift -e l | shift -e r")
        ;;
    l*) master_dir=right 
        focus_toggle=$(herbstlogic "focus -e l | focus -e r")
        shift_toggle=$(herbstlogic "shift -e l | shift -e r")
        ;;
    u*) master_dir=down
        focus_toggle=$(herbstlogic "focus -e u | focus -e d")
        shift_toggle=$(herbstlogic "shift -e u | shift -e d")
        ;;
    d*) master_dir=up
        focus_toggle=$(herbstlogic "focus -e u | focus -e d")
        shift_toggle=$(herbstlogic "shift -e u | shift -e d")
        ;;
esac




############################
## Construction Functions ##
############################

function generate_auto () {

##################################
# Generates the Logic for auto() #
##################################

# This reads easier bottom-to-top!

### IF SPLIT NEEDED ###
only_frame_true=$(herbstlogic "split $split_direction $split_ratio , $shift_toggle , set_layout $stack_layout ")

# ^
# |
# |

### IF SPLIT NOT NEEDED ###
# There are two windows in the master. Get one outta there!
shift_final_master_not_alone=$(herbstlogic "cycle -1 , shift -e $stack_dir , $focus_toggle")
# If window is alone, we're done. Otherwise, get the other window outta there!
shift_final_master_check_alone=$(herbstlogic "$query_window_alone | $shift_final_master_not_alone")
# Shift to master. If in master, shift to stack.
shift_final=$(herbstlogic if "shift -e $master_dir" then "$shift_final_master_check_alone" else "shift -e $stack_dir")

# ^
# |
# |

### Checking if a shift will result in an empty frame.
# If the other frame is empty, just shift the current window over there.
other_frame_empty=$(herbstlogic "$focus_toggle & $query_frame_empty & $focus_toggle & $shift_toggle")
# If the other frame is not empty, proceed...
other_frame_not_empty=$(herbstlogic "$shift_toggle , cycle -1 , $shift_final")
# Check if the other frame is empty, then do something...
window_alone_do=$(herbstlogic "$other_frame_empty | $other_frame_not_empty")
# If the window on the current frame is alone, do somethings, otherwise just shift it over.
frame_not_empty=$(herbstlogic if "$query_window_alone" then "$window_alone_do" else "$shift_final")
# If the current frame has multiple windows, proceed... otherwise it has one window, or no windows...
only_frame_false=$(herbstlogic if "$query_frame_empty" then "$focus_toggle" else "$frame_not_empty")

# ^
# |
# |

### EXECUTE ###
# Attempt to split the frame, if there are two frames, do normal behavior.
do=$(herbstlogic if "$query_frame_n" then "$only_frame_true" else "$only_frame_false")

echo $do
}

function generate_stack_spawn () {

tag_not_empty=$(herbstlogic "focus -e $stack_dir , spawn $@")
tag_empty=$(herbstlogic "spawn $@ , $(generate_auto) , focus_nth 0 , shift -e $master_dir , focus -e $stack_dir")

do=$(herbstlogic if "$query_frame_n" then "$tag_empty" else "$tag_not_empty")

echo $do
}

function generate_master_spawn () {

tag_not_empty=$(herbstlogic "$(generate_auto) , focus -e $master_dir , shift -e $stack_dir , focus -e $master_dir , spawn $@" )

check_tag_empty=$(herbstlogic "$query_frame_n & $query_frame_empty & spawn $@")
do=$(herbstlogic "$check_tag_empty | $tag_not_empty")

echo $do
}

function generate_close () {

check_master_do=$(herbstlogic "focus -e $stack_dir , shift -e $master_dir")
check_stack=$(herbstlogic "$query_frame_empty & remove")
check_master=$(herbstlogic "$query_frame_empty & $check_master_do")
do=$(herbstlogic "focus -e $master_dir , $check_master , focus -e $stack_dir , $check_stack")

echo $do
}

case $1 in
   close)
        shift
        herbstclient close
        close=$(generate_close)
        herbstclient $close
        exit
        ;;
esac

The script for the "herbstlogic" helper is this:

#! /bin/bash

#########################
###### Logic Helper #####
#########################
index_location=/tmp/hlwm_logic_index
echo 0 > $index_location
function herbstlogic () {
   if [ "$1" == "if" ]; then
      herbstlogic_if "$@"
      exit
   fi

   logic_index=$(cat $index_location)
   local hl_and=0
   local hl_or=0
   local hl_chain=0
   local hl_type=""

   if [[ $1 == *'&'* ]] ; then
      hl_and=1
      hl_type=and   
      logic_symbol="&"
   fi
   if [[ $1 == *'|'* ]] ; then
      hl_or=1
      hl_type=or   
      logic_symbol="|"
   fi
   if [[ $1 == *','* ]] ; then
      hl_chain=1
      hl_type=chain   
      logic_symbol=","
   fi
   local sep_n=$(( hl_and + hl_or + hl_chain ))

   if [ $sep_n -gt 1 ]; then
      echo "Too many seperators"
      exit 1
   elif [ $sep_n == 0 ]; then
      echo "$1"
      exit 0
   fi

   local seperator="$hl_type""$logic_index"
   echo "$hl_type" "$seperator" ${1//"$logic_symbol"/"$seperator"}
   echo $(( logic_index + 1 )) > $index_location
}

function herbstlogic_if () {
   if [ $1 == "if" ]; then
      shift
   else
      echo "not an if statement"
      exit 1
   fi

   local condition=$(herbstlogic "$1") 
   shift

   if [ $1 == "then" ]; then
      shift
   else
      echo "couldn't find mathing \'then\' command, error token $1"
      exit 1
   fi

   local consequence=$(herbstlogic "$1")
   shift

   cons=$(herbstlogic "$condition & $consequence")

   if [ -z $1 ]; then
      echo $cons
      exit 0
   elif [ $1 == "else" ]; then
      shift
   else
      echo "couldn't find parse $1"
      exit 1
   fi

   local else=$(herbstlogic "$@")

   echo $(herbstlogic "$cons | $else")
   exit 0
}

# The old documentation for this script is wrong

Last edited by wretch (2012-12-03 03:31:17)

Offline

#294 2012-12-03 01:52:42

ninjaaron
Member
Registered: 2010-12-10
Posts: 296

Re: herbstluftwm

So, this seems very cool (especially that herbstlogic business), but I have some questions.

Wait, so how did you get this to live in the memory with hlwm?
This script is so mind-blowing that I don't quite follow.

autostart usually just executes when you start or reload herbstluftwm and if you use variables in your key-binding assignments, they are usually just stuck at the value they have at runtime... how is this able to dynamically rebind keys without reloading autostart?

edit: p.s. this script uses sed every time you call herbstlogic (which seems to be pretty often). The process police are on their way to your place of residents!  big_smile

Last edited by ninjaaron (2012-12-03 02:34:45)

Offline

#295 2012-12-03 03:14:24

wretch
Member
From: pdx
Registered: 2012-11-24
Posts: 22

Re: herbstluftwm

ninjaaron wrote:

So, this seems very cool, but I have some questions.

Wait, so how did you get this to live in the memory with hlwm?
This script is so mind-blowing that I don't quite follow.

Aye, I suppose I could've given even more explaination. Part of the problem is our wanting-ness to read it like a script. The script is purely functional and generates static information from the user settings, so its hard to read it top to bottom.

ninjaaron wrote:

autostart usually just executes when you start or reload herbstluftwm and if you use variables in your key-binding assignments, they are usually just stuck at the value they have at runtime...

Exactly. The "script" just generates a single command which is used for each key:

$ generate_auto
or or15 and and14 cquery 1 nframe and14 chain chain2 split horizontal 0.6667 chain2 or or1 shift -e l or1 shift -e r chain2 set_layout vertical or15 or or13 and and12 cquery 0 nwindows and12 or or0 focus -e l or0 focus -e r or13 or or11 and and10 cquery 1 nwindows and10 or or9 and and7 or or0 focus -e l or0 focus -e r and7 cquery 0 nwindows and7 or or0 focus -e l or0 focus -e r and7 or or1 shift -e l or1 shift -e r or9 chain chain8 or or1 shift -e l or1 shift -e r chain8 cycle -1 chain8 or or6 and and5 shift -e left and5 or or4 cquery 1 nwindows or4 chain chain3 cycle -1 chain3 shift -e right chain3 or or0 focus -e l or0 focus -e r or6 shift -e right or11 or or6 and and5 shift -e left and5 or or4 cquery 1 nwindows or4 chain chain3 cycle -1 chain3 shift -e right chain3 or or0 focus -e l or0 focus -e r or6 shift -e right

This string is exactly what gets passed to my keybind call in autostart, given my user values.

Notice that every command in that string is a hlwm command, along with those seperators like 'and14', which are generated with my helper script to ensure uniqueness. Those are just seperators for the and, or, and chain commands. The entire string is sent to the window manager, where it stays until a key is pressed. That's why no new processes are created on a bound key.

ninjaaron wrote:

how is this able to dynamically rebind keys without reloading autostart?

It doesn't. Each key is simply bound to a command string like the one above. So that string will be exactly the same until one of the user settings values is changed. When this happens, you have to manually reload autostart, which regenerates the string. This is what it looks like in my autostart:

# Loads the function definitions into the autostart bash process
source /path/to/dynamic_script

# Generate_auto is a function sourced from the above file.
keybind $Mod-Shift-Enter $(generate_auto)

I hope that at least somewhat clears that question up.



As for how that string performs the same functionality as your script does, that's a different question.

Let's look at the function generate_stack_spawn for example:

tag_not_empty=$(herbstlogic "focus -e $stack_dir , spawn $@")
tag_empty=$(herbstlogic "spawn $@ , $(generate_auto) , focus_nth 0 , shift -e $master_dir , focus -e $stack_dir")

do=$(herbstlogic if "$query_frame_n" then "$tag_empty" else "$tag_not_empty")

echo $do

Think of tag_not_empty and tag_empty as functions. In this case, they are both chain commands consisting of more commands. So $do simply checks if the current tag is empty, then executes either of those chains ($query_frame_n just points to a command that I patched in that returns 0 if the frame is empty). An if statement can be expressed in (sequencial) boolean logic using only and's and or's, which coincidentally, hlwm now has:

(( condition & consequence) | else_consequence)
--or--
(( tag_empty? & do_tag_empty_function) | do_tag_not_empty_function )

The first term in the OR is ( tag_empty? & do_tag_empty ). If tag_empty? fails, then do_tag_empty doesn't get executed, and the entire term returns a nonzero. This means the other term of the OR gets a chance to execute. Of course, if tag_empty? passes, then do_tag_empty executes (which hopefully returns 0) this means the whole first term returns 0, and do_tag_not_empty doesn't execute.

Following that logic (hee, a pun), you can start at the DO function in generate_auto, and work your way up given whatever state the wm is in, like a tree.

So we can think of each variable is a function, and herbstlogic just relates each function using logic. Hlwm just decends this tree following branches that depend on the current state of the wm when a key is pressed.

Note: Most commands in hlwm return a proper value indicating their status, meaning condition in the above if..then logical statement can be nearly any hlwm command.



Like I said, this script was not all that intuitive for me to write, either. But it works, so if I can improve my herbstlogic helper script enough so that it makes scripting in this method easy, it's probably something I would use.

[edit]
edit to respond to your edit:

ninjaaron wrote:

edit: p.s. this script uses sed every time you call herbstlogic (which seems to be pretty often). The process police are on their way to your place of residents!

Yeah! But the script is only executed when you reload hlwm. But yeah, you're right, I could've used a better form of string manipulation smile

edit pt 2. Okay cool. I modified the above script using bash built-ins instead of grep and sed, and my autostart executes much faster. Thanks for pointing that out.

Last edited by wretch (2012-12-03 04:18:55)

Offline

#296 2012-12-03 04:45:04

ninjaaron
Member
Registered: 2010-12-10
Posts: 296

Re: herbstluftwm

Ah... this is wonderful. Insane, but wonderful. You're effectively writing an internal scripting language for herbstluftwm in bash... so the script isn't the script... the output is the script... crazy stuff. It's like herbstluftwm Inception. It's So Meta, Even This Acronym... wink

So cquery lets you get all the information from hc dump in a form digestible by logical operators for the hc and/or commands, and the rest, as they say, is history?

I'm still not totally clear on what commands like "and14" do, or really what they even are. There's nothing about that in the man page. Is this part of your patch?

In any event, I hope your query patch gets incorporated upstream since it makes the hc and/or operators much more useful, as you've shown.

Last edited by ninjaaron (2012-12-03 04:53:04)

Offline

#297 2012-12-03 05:20:23

The Compiler
Member
From: Switzerland
Registered: 2011-05-01
Posts: 214
Website

Re: herbstluftwm

@ninjaaron: the {and,or}[0-9][0-9] are just command seperators. So instead of hc chain , foo , bar , baz he just does hc chain and01 foo and01 bar and01 baz

@wretch: This is pretty damn sick. You should spam the mailinglist with all the patches you didn't submit yet. I bet thorsten soon(-ish) has the time to include them.


>>> from __future__ import braces
  File "<stdin>", line 1
SyntaxError: not a chance

Offline

#298 2012-12-03 06:28:25

wretch
Member
From: pdx
Registered: 2012-11-24
Posts: 22

Re: herbstluftwm

@ninjaaron @the_compiler: Thanks dudes. All of the patches I've written I've submitted to the mailinglist except for query and cquery. The reason for this is I intend to change the syntax first for make adding additional queries easier, and to also add functionality:

herbstclient query 'query_name' [ 'comparison' 'value' ]
+ if  [ 'comparison' 'value' ] is omitted, the value of the query is printed to stdout.
+ otherwise, the value of the query is compared to 'value' using any comparison 
+    operator ( == , != , < , > , <= , >= ) and the status of comparison is returned.

This will make the syntax more obvious, 'cquery' wont be needed, and yay, more comparisons!
Shouldn't take too long to write, but hey, its finals week. Should have a base for the 'query' command in the next week or two, and then I'll see if Thorsten approves smile

[Edit] I think I've gotten my herbstlogic-bash-metascripting functions to the point where writing meta-scripts is cake. Since writing my own scripting launguage (that I was purposfully planning on it being very restricted, no extensions beyond logic) would be no good for things like parsing user input before generating a command for the hlwm isl (internal scripting launguage), I think I'll just stick to bash and really polish it, and make it intuitive. Ill post more info once I clean up some stuff.

Ps. @ninjaaron @the_compiler. Yes, and14 is just a seperator for and (instead of using a ","). I did this so each atom has a unique seperator, in case I ran into any scope problems (since there is LOTS of nested logical statements).


[Edit]

ninjaaron wrote:

So cquery lets you get all the information from hc dump in a form digestible by logical operators for the hc and/or commands, and the rest, as they say, is history?

Yeah, kind of. I have only added enough queries thusfar to make the script work. Not all the information from dump is available (nor do I think it will be, queries and dump serve different purposes).
As far as "digestible", yes. And and or just see cquery as another command. The secret is in cquery's return value (which currently is zero if the value matches).

Last edited by wretch (2012-12-03 20:49:45)

Offline

#299 2012-12-03 07:45:08

ninjaaron
Member
Registered: 2010-12-10
Posts: 296

Re: herbstluftwm

wretch wrote:

As far as this logical "scripting language," I'm gunna get started on an "interpreter," written in C that reads from just a script file (air quotes for emphasis smile ). I'll post a link to a git once I get a baseline for it. In the meantime, if anyone is interested in giving some input, here is my intended syntax: [...]

This is totally awesome, but be sure to make the syntax resembles either C or Bash, as those are the "native languages" of herbstluftwm, so to speak.

Other thought: it would be ideal if, like herbstluftwm, there was a convenient way to use this proposed program as part of a shell script, rather than having to play in its own sand box. Whatever you make, I assume it will not be as powerful as existing shell languages. I'm having trouble visualizing how that would be implemented, but I'll leave that to the CS majors to figure out.

Other other thought: the ultimate evolution of this idea must inevitably be the server-side implementation; that is, this functionality built into herbsluftwm itself, so you could run a command like this:

herbstclient do << EOF
if [ $(query active_frame) = 1 ]; then
  shift -e left
else
  shift -e right
fi

focus -i d
EOF

Not that this has to be the exact syntax (or that these specific commands would do anything anyone would ever consider useful), but you see where I'm going with this. This should ultimately become a feature hlwm, rather than a weird hacky program to create mad-science-esque chain/and/or commands. If that seems too ambitious at this point, at least work on the project with that eventuality in mind.

Glad you are bringing these interesting ideas to the hlwm community!

Last edited by ninjaaron (2012-12-03 07:51:46)

Offline

#300 2012-12-03 08:15:12

ninjaaron
Member
Registered: 2010-12-10
Posts: 296

Re: herbstluftwm

@The Compiler
I finally compiled your sig and my mind was blown. What is it?

Last edited by ninjaaron (2012-12-03 08:16:45)

Offline

Board footer

Powered by FluxBB