Preface

Goal: Separate Main Flow, Code, and Data.

So anyone can focus to alter special customization in Main Script, without changing the whole stuff.

Reading

Before you jump off to scripting, you might desire to read this overview.

All The Source Code:

Impatient coder like me, like to open many tab on browser.


Modularized HerbstluftWM in Many Languages

This article is one part of a collection. All integrated, on related to another. So we can compare each other quickly.

Tutorial/ Guidance/ Article: [ Modularized Overview ] [ BASH ] [ Perl ] [ Python ] [ Ruby ] [ PHP ] [ Lua ] [ Haskell ]

Source Code Directory: [ BASH ] [ Perl ] [ Python ] [ Ruby ] [ PHP ] [ Lua ] [ Haskell ]


Directory Structure

Directory Structure has been explained in preface. This figure will explain how it looks in PHP script directory.

HerbstluftWM: Directory Structure


Modularizing in PHP

Nothing to say here. PHP is simple

Declare a module

No need to explicitly define what to export.

Nothing to write in helper Script

Call a module

require_once('helper.php');

System Calls

Here we wrap herbstclient system call in a function named hc.

helper.php

function hc($arguments) {
    system("herbstclient $arguments");
}

autostart.php

# Read the manual in $ man herbstluftwm
hc('emit_hook reload');

# gap counter
system("echo 35 > /tmp/herbstluftwm-gap");

Array: Tag Names and Keys

config.php

$tag_names = range(1, 9);
$tag_keys  = array_merge(range(1, 9), [0]);

HerbstluftWM: Tag Status


Hash: Color Schemes

Using key-value pairs, a simple data structure.

assets/gmc.php

$color = array(
    'white' => '#ffffff',
    'black' => '#000000',

    'grey50'  => '#fafafa',
    'grey100' => '#f5f5f5',
);

autostart.php

# background before wallpaper
system("xsetroot -solid '${color['blue500']}'");

View Source File:

Similar Code: [ BASH Color ] [ Perl Color ] [ Python Color ] [ Ruby Color ] [ PHP Color ] [ Lua Color ] [ Haskell Color ]


Hash: Config

The Hash in Config is very similar with the colors above. Except that it has string interpolation all over the place.

config.php

# Modifier variables
$s = 'Shift';
$c = 'Control';
$m = 'Mod4';
$a = 'Mod1';

$keybinds = array(
  # session
    "$m-$s-q" => 'quit',
    "$m-$s-r" => 'reload',
    "$m-$s-c" => 'close'
)

This config will be utilized in main script as shown in the following code.

autostart.php

do_config("keybind",   $keybinds);
do_config("keybind",   $tagskeybinds);
do_config("mousebind", $mousebinds);
do_config("attr",      $attributes);
do_config("set",       $sets);
do_config("rule",      $rules);

View Source File:

Similar Code: [ BASH Config ] [ Perl Config ] [ Python Config ] [ Ruby Config ] [ PHP Config ] [ Lua Config ] [ Haskell Config ]


Processing The Hash Config

This is the heart of this script.

This do-config function has two arguments, the herbstclient command i.e “keybind”, and hash from config. I can see how simple and clean, PHP is.

helper.php

function do_config($command, $hash) {
    # loop over hash
    foreach ($hash as $key => $value) {
        hc($command.' '.$key.' '.$value);

        # uncomment to debug in terminal
        # echo $command.' '.$key.' '.$value."\n";
    }
}

Debug Herbstclient Command

I do not remove line where I do debug when I made this script, so anyone can use it later, avoid examining blindly. Sometimes strange things happen. Just uncomment this line to see what happened.

        echo $command.' '.$key.' '.$value."\n";

You can see the debugging result in figure below.

HerbstluftWM: Debug Command

View Source File:

Similar Code: [ BASH Helper ] [ Perl Helper ] [ Python Helper ] [ Ruby Helper ] [ PHP Helper ] [ Lua Helper ] [ Haskell Helper ]


Setting the Tags

Nothing special here, PHP read all exported variable from modules, but PHP function do not read the varable unless it defined global. The only workaround I found is by passing it as function arguments.

helper.php

function set_tags_with_name($tag_names, $tag_keys) {
    hc("rename default '$tag_names[0]' 2>/dev/null || true");
    
    foreach($tag_names as $index=>$value) {
        hc("add '$value'");
        
        $key = $tag_keys[$index];
        if (!empty($key)) {
            hc("keybind Mod4-$key use_index '$index'");
            hc("keybind Mod4-Shift-$key move_index '$index'");
        }
    }
}

Launch the Panel

Two more functions left, it is do_panel and startup_run.

This two also be easy to do in PHP. helper.php

function do_panel() {
    $panel   = __dir__."/../bash/dzen2/panel.sh";
    if (!is_executable($panel))
        $panel = "/etc/xdg/herbstluftwm/panel.sh";

    $raw = shell_exec('herbstclient list_monitors | cut -d: -f1');
    $monitors = explode("\n", trim($raw));

    foreach ($monitors as $monitor) {
        # start it on each monitor
        system("$panel $monitor > /dev/null &"); // no $output
    }
}

Run Baby Run

This is the last part. It is intended to be modified. Everyone has their own personal preferences.

startup.php

function startup_run() {
    $command = 'silent new_attr bool my_not_first_autostart';
    system("herbstclient $command", $exitcode);

    # without redirection your app WILL HANG !
    if ( ! ((bool) trim($exitcode)) ) {
      # non windowed app
        system("compton > /dev/null &");
        system("dunst > /dev/null &");
        system("parcellite > /dev/null &");
        system("nitrogen --restore > /dev/null &");
        system("mpd > /dev/null &");

      # windowed app
        exec("xfce4-terminal > /dev/null 2>&1 &");
        exec("firefox > /dev/null 2>&1 &");
        exec("geany > /dev/null 2>&1 &");
        exec("thunar > /dev/null 2>&1 &");
    }
}

Specific PHP Issue

Without redirection, your script will hang.

Be aware of the > /dev/null & part in PHP. It is mandatory for running background processing. The PHP manual has said it clearly.

View Source File:

Similar Code: [ BASH Startup ] [ Perl Startup ] [ Python Startup ] [ Ruby Startup ] [ PHP Startup ] [ Lua Startup ] [ Haskell Startup ]


Putting It All Together.

The last part is going to main script and putting it all back together.

Now the flow is clear

Header Part: autostart.php

require_once('assets/gmc.php');
require_once('helper.php');
require_once('config.php');
require_once('startup.php');

Procedural Part: autostart.php

# background before wallpaper
system("xsetroot -solid '${color['blue500']}'");

# Read the manual in $ man herbstluftwm
hc('emit_hook reload');

# gap counter
system("echo 35 > /tmp/herbstluftwm-gap");

# do not repaint until unlock
hc("lock");

# standard
hc('keyunbind --all');
hc("mouseunbind --all");
hc("unrule -F");

set_tags_with_name($tag_names, $tag_keys);

# do hash config
do_config("keybind",   $keybinds);
do_config("keybind",   $tagskeybinds);
do_config("mousebind", $mousebinds);
do_config("attr",      $attributes);
do_config("set",       $sets);
do_config("rule",      $rules);

# unlock, just to be sure
hc("unlock");

# launch statusbar panel
do_panel();

# load on startup
startup_run();

View Source File:

Similar Code: [ BASH autostart ] [ Perl autostart ] [ Python autostart ] [ Ruby autostart ] [ PHP autostart ] [ Lua autostart ] [ Haskell autostart ]


Happy Configuring.