Deblan blog

Tag #Configuration

i3wm - menu d'applications

i3wm est un Tiling Window Manager, c'est à dire que l'ensemble des fenêtres de mon environnement de travail est géré de telle sorte que l'espace soit le plus optimisé possible. Dans ce type de configuration (et quelque soit le TWM), c'est au clavier que tout se pilote. Le problème c'est qu'au fond d'un lit, sous une couette, c'est chiant de faire des raccourcis claviers.

Avant de passer à i3wm, j'utilisais Fvwm-Crystal qui possédait un menu d'application pour lancer les logiciels à la souris. i3wm n'en dispose pas et ça me gène.

Pour répondre à cette problématique, il existe un outil qui se nomme SSHPlus. C'est un script écrit en python qui permet de générer un menu comme celui ci.

Manuberro de Absolacom a rédigé un excellent article qui explique comment configurer SSHPlus. Cependant, je suis un gros flemmard et j'ai envie générer automatiquement le menu d'applications. Pour ce faire, j'ai écrit un script PHP qui va piocher dans le répertoire /usr/share/menu/ de ma Debian et qui sort une conf au format SSHPlus.

Il suffira donc de lancer le script et de rediriger la sortie standard vers ~/.sshplus.

$ /chemin/vers/script.php > ~/.sshplus

Voici le script (vous pouvez modifier les fonctions prepend_config() et append_config() pour insérer des configurations spéciales) :

#!/usr/bin/env php
<?php

function append_config()
{
    $config = <<<EOF
sep
folder:Session
Éteindre|sudo|shutdown -h now
Reboot|sudo|shutdown -r now
folder:
EOF;

    return sprintf("%s\n", $config);
}

function prepend_config()
{
    $config = <<<EOF
EOF;

    return sprintf("%s\n", $config);
}

function is_x11_application($setting)
{
    return 0 !== preg_match('`needs="X11"`i', $setting);
}

function get_title($setting)
{
    return preg_replace('`.*title="([^"]+)".*`is', '$1', $setting);
}

function get_sections($setting)
{
    return explode('/', preg_replace('`.*section="([^"]+)".*`is', '$1', $setting));
}

function get_command($setting)
{
    return preg_replace('`.*command="([^"]+)".*`is', '$1', $setting);
}

function get_info($setting)
{
    return array(
        'title'    => get_title($setting),
        'command'  => get_command($setting),
        'sections' => get_sections($setting)
    );
}

function cleanup_config($config)
{
    return preg_replace("`folder:[^\n]+\nfolder:\n`", '', $config);
}

function get_config($menus)
{
    $config = '';

    foreach ($menus as $k => $menu) {
        if (is_array($menu)) {
            if (!empty($menu)) {
                $config.= sprintf("folder:%s\n%sfolder:\n", $k, get_config($menu));
            }
        } else {
            $config.= sprintf("%s\n", $menu);
        }
    }

    return cleanup_config($config);
}

$setting_files = glob('/usr/share/menu/*');
$menus         = array();
$apps          = array();
$final_menus   = array();

foreach ($setting_files as $k => $file) {
    $_settings = explode('package', file_get_contents($file));

    foreach ($_settings as $setting) {
        if (is_x11_application($setting)) {
            $info     = get_info($setting);
            $sections = $info['sections'];

            if (!in_array($sections, $menus)) {
                $menus[] = $sections;
            }

            $apps[] = $info;
        }
    }
}

foreach ($menus as $sections) {
    foreach ($sections as $k => $section) {
        if (!$k && !isset($final_menus[$section])) {
            $final_menus[$section] = array();
        }

        if ($k && !isset($final_menus[$sections[$k-1]][$section])) {
            $final_menus[$sections[$k-1]][$section] = array();
        }
    }
}

foreach ($apps as $app) {
    $eva = '$final_menus';

    foreach ($app['sections'] as $s) {
        $eva.= '["'.addslashes($s).'"]';
    }

    $full_command = explode(' ', addslashes($app['command']));
    $command = array_shift($full_command);
    $params  = implode(' ', $full_command);

    $eva.= '[] = "'.$app['title'].'|'.$command.'|'.$params.'";';

    eval($eva);
}

$config = prepend_config().get_config($final_menus).append_config();

echo $config;

J'admet que le code n'est pas très propre mais il fonctionne, c'est l'essentiel :)

Pour lancer SSHPlus au démarrage de i3wm, il suffira de placer dans ~/.i3/config cette ligne : exec /chemin/vers/sshplus.py (n'oubliez pas de faire un chmod +x sur le fichier et vous devrez disposer du paquet python-appindicator).


i3wm - barre de status

Il y a quelques semaines, j'ai changé de gestionnaire de fenêtres sur ma Debian. De Fvwm-crystal (que je recommande beaucoup) à i3wm, j'ai un full tiling manager et c'est vraiement nickel.

J'ai fais quelques lignes de code pour modifier la barre de status et je vais les paratager.

i3wm status bar

Mais avant tout, on va configurer le minimum vital pour que ça fonctionne :

Dans le bloc bar { ... } de votre fichier de configuration, remplacez status_command i3status par status_command ~/.i3/i3status.sh.

~/.i3/i3status.sh doit contenir :

#!/bin/sh

php ~/.i3/i3status.php

i3status | while read LINE; do
	php ~/.i3/i3status.php
done

Vous l'aurez remarqué, j'ai décidé d'utiliser du php. Pour ceux que ça ne dérange pas et qui n'ont pas encore le nécessaire :

$ su -
# aptitude update
# aptitude install php5

Il ne reste plus que de placer dans ~/.i3/i3status.php le code suivant :

<?php
// J'ai des soucis d'encodage sur le blog, le code avec les caractères en utf8 sont dispos ici : http://wall.deblan.fr/x10a/php/1/

define('SEPARATOR', ' # ');

render(
	get_the_notification(), 
	get_ips(array('eth0', 'wlan0')), // indiquez les interfaces susceptibles d'être utilisées et dont vous voulez connaître les IP (si elles en ont)
	get_the_acpi(),
	get_the_date()
);


/**
 *
 * IP
 *
 */
function get_ips($ifaces=array()) {
	$ips = array();
	foreach($ifaces as $iface) {
		preg_match('`inet ([^\s]+)`', shell_exec('ip addr show '.escapeshellcmd($iface)), $inet);

		if($inet) {
			$ips[] = '['.$iface.'] '.$inet[1];
		}
	}

	return implode(SEPARATOR, $ips);;
}

function ips() {
	echo get_ips();
}


/**
 *
 * Notification
 *
 */
function get_the_notification() {
	$final = '';

	if(file_exists($file=getenv('HOME').'/.i3/notification')) {
		$info = json_decode(file_get_contents($file));

		if($info->count == 15) {
			unlink($file);
		}
		else {
			$info->count++;

			file_put_contents(
				$file, 
				json_encode($info)
			);

			$message = '['.$info->title.'] '.$info->message;

			$final.= $message;
		}
	}

	return $final;
}

function the_notification() {
	echo get_the_notification();
}


/**
 *
 * Date
 *
 */
function get_the_date() {
	return date('H:i:s'.SEPARATOR.'d/m/Y');
}

function the_date() {
	echo get_the_date();
}

/**
 *
 * Acpi
 *
 */
function get_the_acpi() {
	$acpi = shell_exec('acpi');

	if(!preg_match('`Discharging`', $acpi)) {
		preg_match('`([0-9]+%)`', $acpi, $values);

		if(!empty($values)) {
			$charging = $values[1];
			return $charging.'?';
		}

		return '';
	}
	
	preg_match('`([0-9]+%), ([0-9]+:[0-9]+)`', $acpi, $values);

	$charging = $values[1].'?';
	$times    = str_replace(':', 'h', $values[2]);

	return implode(' ', array($charging, $times));
}

function the_acpi() {
	echo get_the_acpi();
}


/**
 *
 * Render
 *
 */
function get_the_render($values) {
	foreach($values as $k => $v) {
		if(empty($v)) {
			unset($values[$k]);
		}
	}

	return implode(SEPARATOR, $values).SEPARATOR;
}

function render() {
	echo get_the_render(func_get_args());
}

J'ai un système de notification qui permet de faire ça :

i3wm status bar

Voici le script qui permet de générer la notification :

#!/usr/bin/php5
<?php

$file = getenv('HOME').'/.i3/notification';

$info = array(
	'title'   => $argv[1],
	'message' => $argv[2],
	'count'   => 0
);

file_put_contents(
	$file, 
	json_encode($info)
);
/chemin/vers/le/script titre "Je suis une notification"

Terminé :)