Preface

Goal: Separate Main Flow, Code, and Data.

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

Which PHP

Since there many PHP version. I should say that I use PHP7. But don’t worry, it is portable. I do not use any sophisticated code.

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.

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.

helper.php

function set_tags_with_name()
{
    global $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__."/panel-lemonbar.php";
    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('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();

# 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 ]


Coming up Next

After the Window Manager, comes the Panel.


Happy Configuring.