Rendez-vous sur Arrakis, Le site perso d'un hacker libriste curieux crêpophile étourdi
Le 19/06/2019 à 15:43 dans /Logiciel-libre/Suckless/dwm/

dwm

Présentation

Dwm est un gestionnaire de fenêtres de type tiling (Gestionnaire de fenêtres en tuile). L'emplacement des fenêtres est automatique, et optimisé pour que vous n'ayez pas à vous préoccuper de les organiser. De plus, sachez que tout peut être réalisé au clavier, ce qui est un gain de temps non négligeable.

Fidèle à la philosophie suckless, le programme est écrit en C, dans un code très propre et léger, qui assure rapidité et fiabilité. La configuration se déroule dans un fichier à la syntaxe simplifiée : le config.h dont nous parlerons ensuite. Il est donc accessible malgré les apparences. Un système de patches permet d'étendre les fonctionnalités de dwm.

Debian propose un paquet de démonstration. Nous verrons dans la suite du document comment configurer dwm, et comment reconstruire un paquet proprement. Mais avant de commencer, voici quelques aperçus (cliquer pour agrandir):

Utilisation

Par défaut, vous disposez par défaut de 9 vues, ou plutôt 9 étiquettes à attribuer aux fenêtres. Oubliez le paradigme habituel des espaces de travail : avec dwm, vous collerez des étiquettes (tags) sur les fenêtres, comme si vous leur donniez un label.

Une fois l'habitude prise, vous comprendrez pourquoi dwm veut dire Dynamic window manager.

Chaque petit chiffre correspond à une étiquette (ou tag), qui est affecté à une fenêtre. Vous donnez à chaque fenêtre une étiquette, allant de 1 à 9, pour ensuite afficher les fenêtres portant la même étiquette, ou les fenêtres portant les étiquettes 1 et 2, etc.

À vous de choisir si vous souhaitez afficher les fenêtres qui ont l'étiquette 1, 3, ou bien 3 et 4, ou encore “net” et “video”… En gros, ça ne marche pas exactement comme un espace de travail, vous choisissez les fenêtres que vous voulez voir, et pour cela, ce n'est plus vous qui vous déplacez, c'est vous qui demandez aux fenêtres “HEP! viens par là!

Concrètement, cela veut dire que lorsque votre “vue” commence à être trop chargée, qu'il y a trop de fenêtre, vous pouvez donner à certaines d'entre elles une autre étiquette. Cette autre étiquette, pous pourrez plus tard choisir de l'afficher. Maintenant, vous changez d'activité. Vous choisissez une nouvelle étiquette avec Alt+chiffre, et ouvrez de nouvelles fenêtres. Mais vous voudriez pouvoir revenir à l'ancienne vue rapidement, pour retrouver votre ancienne activité. Avec le raccourci Alt+Tab, vous affichez une nouvelle vue vide. Pour revenir à la précédente, il suffit de refaire Alt+Tab.

En somme, une “vue” correspond à un arrangement particulier des fenêtres, une sélection de plusieurs étiquettes. Cette vue pourra être retrouvée à tout moment.

Les raccourcis claviers

Dwm a été pensé pour être entièrement piloté au clavier. Attention toutefois, ne pensez pas utiliser dwm en utilisant les tags comme de simples espaces de travail, ce serait passer à côté de l'utilité de dwm. Ainsi, vous aurez la plupart du temps plusieurs fenêtres ayant un tag particulier à la fois d'affichés. Essayez, une fois que vous y serez habitué, vous serez beaucoup plus efficaces.

Voici un descriptif des raccourcis clavier par défaut. La touche Mod1 désigne la touche Alt.

Attention Vous noterez que tous les raccourcis ne fonctionnent pas avec un clavier français classique (azerty). Nous y remédierons très rapidement, soyez rassuré! ☺

De l'usage des étiquettes

Je vais présenter ici une utilisation de dwm que je trouve extrêmement pratique : l'utilisation avancée des étiquettes.

Rappelons tout d'abord que l'on peut alterner entre la vue actuelle et la vue précédente avec mod+TAB.

À chaque fenêtre est attribuée une étiquette (tag). Afin de consulter/cacher une fenêtre, je ne me déplace pas dans son “tag” avec mod+chiffre, mais j'affiche les fenêtres ayant cette étiquette avec ctrl+mod+chiffre.

Par contre, je peux créer une seconde “vue” en allant choisissant d'autres fenêtres. En faisant mod+TAB, j'alterne rapidement entre les vues.

Prenons un exemple. J'écris actuellement cette page dans un terminal ayant l'étiquette 1 :

Je souhaite afficher la fenêtre de conversation qui a l'étiquette 3. Je tape alors mod+ctrl+3 :

Désormais, je souhaite naviguer sur internet, mon navigateur a l'étiquette 2. Je tape alors mod+2

Pour revenir à la vue précédente, qui contenait la fenêtre de discussion et le terminal où j'écris cette page (étiquettes 1 et 3), il me suffit de faire mod+TAB

Pour retourner au navigateur, on refait mod+TAB.

Par défaut, les raccourcis claviers ne sont peut-être pas les plus pratiques. Afin de simplifier la vie, je vous propose d'échanger les raccourcis, afin que mod+chiffre affiche un tag en plus des autres, et mod+ctrl+chiffre n'affiche que les fenêtres ayant l'étiquette “chiffre”.

Je vous laisse le fichier diff associé, tiré d' ici. Pour l'appliquer : patch -p1 < config.h.diff après l'avoir enregistré dans le fichier config.h.diff. Voir la section appliquer un patch.

Remarquez que sa lecture est très simple, les lignes précédées d'un - sont supprimées, les lignes précédées d'un + sont ajoutées à la place.

--- a/config.h  2013-01-25 05:20:04.618352250 +1100
+++ b/config.h  2013-01-25 05:22:40.357508538 +1100
@@ -37,8 +37,8 @@ static const Layout layouts[] = {
/* key definitions */
#define MODKEY Mod1Mask
#define TAGKEYS(KEY,TAG) \
-   { MODKEY,             KEY,    view,       {.ui = 1 << TAG} }, \
-   { MODKEY|ControlMask,     KEY,    toggleview,   {.ui = 1 << TAG} }, \
+   { MODKEY,             KEY,    toggleview,   {.ui = 1 << TAG} }, \
+   { MODKEY|ControlMask,     KEY,    view,       {.ui = 1 << TAG} }, \
  { MODKEY|ShiftMask,       KEY,    tag,        {.ui = 1 << TAG} }, \
  { MODKEY|ControlMask|ShiftMask, KEY,  toggletag,    {.ui = 1 << TAG} },
@@ -97,8 +97,8 @@ static Button buttons[] = {
  { ClkClientWin,     MODKEY,     Button1,    movemouse,    {0} },
  { ClkClientWin,     MODKEY,     Button2,    togglefloating, {0} },
  { ClkClientWin,     MODKEY,     Button3,    resizemouse,    {0} },
-   { ClkTagBar,      0,        Button1,    view,         {0} },
-   { ClkTagBar,      0,        Button3,    toggleview,     {0} },
+   { ClkTagBar,      0,        Button1,    toggleview,     {0} },
+   { ClkTagBar,      0,        Button3,    view,         {0} },
  { ClkTagBar,      MODKEY,     Button1,    tag,        {0} },
  { ClkTagBar,      MODKEY,     Button3,    toggletag,    {0} },
};

Installation

Dwm est téléchargeable depuis le site officiel. Choisissez alors l'archive la plus récente. Le code est tellement soigné qu'il y a peu de dangers, et la compilation est très simple.

Il est aussi possible de récupérer la dernière version en développement de dwm avec git :

git clone https://git.suckless.org/dwm

Vous obtenez alors un dossier contenant les sources dwm. Déplacez-vous dans ce dossier puis entrez make && make install.

Si vous ne souhaitez pas installer les exécutables dans /usr/local/bin, vous pouvez toujours préciser le chemin du dossier de destination dans le fichier config.mk, à la ligne PREFIX =.

Vous aurez besoin des bibliothèques x11 et xinerama (disponibles par défaut sous OpenBSD, paquets libx11-dev et libxinerama-dev sous debian).

Démarrer une session dwm

Voyons comment démarrer une session dwm.

Avec GDM

Par défaut, gdm va lire le fichier /usr/share/xsession/dwm.desktop pour démarrer une session dwm. Cela n'est pas toujours pratique, surtout si plusieurs utilisateurs veulent utiliser des configurations de dwm qu'ils ont chacun compilés dans leur dossiers personnels.

Une solution possible est de créer de nouveaux fichiers de démarrage sur le même modèle que dwm.desktop, en remplaçant ce qui est exécuté par un script écrit pas vos soins.

Par exemple, créons une session pour l'utilisateur paskal (qui se reconnaîtra ☺). On va alors mettre dans un fichier /usr/share/xsession/dwm- paskal.desktop :

[Desktop Entry]
Encoding=UTF-8
Name=Dwm-paskal
Comment=Dynamic window manager
Exec=/home/paskal/startdwm.sh
Icon=dwm
Type=XSession

Et dans le dossier de paskal, on a créé le fichier /home/paskal/startdwm.sh

#!/bin/sh
# Lancement automatique de firefox
firefox &;
# Lancement de mon dwm perso.
exec /home/paskal/dwm-6.0/dwm

Profitez-en pour mettre dans ce script des commandes que vous voulez lancer automatiquement. Il faut juste penser à mettre un “&” à la fin de la ligne.

Avec xenodm (XDM)

Il faut juste ajouter une ligne “dwm” dans le fichier ~/.xsession. Ce fichier ressemblera donc à ça :

#!/bin/sh
. ~/.profile 
xsetroot -solid steelblue &
xset b off &
xrdb -merge ~/.Xresources &
sct 4500 1>/dev/null &
numlockx &
xidle -timeout 900 -program "/usr/X11R6/bin/xlock -mode space" &
dunst &
mpd &
xbanish &
slstatus &
dwm

Pensez à mettre des “&” en fin de chaque ligne pour ne pas bloquer le démarrage, sauf pour dwm à la fin ;).

Avec un script de démarrage

Avec slim ou simplement en tapant startx, ce sera le fichier ~/.xinitrc qui sera lu. Il sera identique au fichier .xsession évoqué au-dessus, mais aura seulement un autre nom. Cette méthode ne fonctionne pas sous OpenBSD pour des raisons de sécurité et permissions afin de lancer une session X.

Configuration

La configuration de dwm se déroule dans le fichier config.h. Ce dernier est créé après la première compilation : c'est une simple copie du fichier config.def.h, vous pouvez donc le faire avant la première compilation ;)

Je vous invite à lire la documentation de suckless à ce sujet.

Le fichier config.def.h est le fichier de configuration par défaut. Copiez-le en config.h, c'est alors ce fichier qui sera utilisé pour la configuration.

config.h exemple

Le config.h est commenté et informe du rôle de chaque ligne. Je vous propose une configuration ci-dessous qui ajoute et modifie quelques éléments à titre d'exemple.

/* See LICENSE file for copyright and license details. */

/* appearance */
static const unsigned int borderpx  = 2;      /* border pixel of windows */
static const unsigned int snap    = 32;     /* snap pixel */
static const int showbar        = 1;      /* 0 means no bar */
static const int topbar         = 1;      /* 0 means bottom bar */
static const char *fonts[]        = { "Hack:size=9" };
static const char dmenufont[]     = "Hack:size=9";
static const char col_bg[]        = "#002b38";
static const char col_normfg[]    = "#839496";
static const char col_selborder[]   = "#586e75";
static const char col_selbg[]     = "#073642";
static const char col_selfg[]     = "#fdf6e3";
static const char *colors[][3]    = {
    /*           fg       bg       border   */
    [SchemeNorm] = { col_normfg, col_bg, col_bg },
    [SchemeSel]  = { col_selfg, col_selbg,  col_selborder  },
};

/* tagging */
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9"};

static const Rule rules[] = {
    /* xprop(1):
    *   WM_CLASS(STRING) = instance, class
    *   WM_NAME(STRING) = title
    */
    /* class    instance    title     tags mask     isfloating   monitor */
    { "Crawl-tiles",     NULL,     NULL,     0,        True,      -1 },
};

/* layout(s) */
static const float mfact     = 0.55; /* factor of master area size [0.05..0.95] */
static const int nmaster     = 1;    /* number of clients in master area */
static const int resizehints = 1;    /* 1 means respect size hints in tiled resizals */

static const Layout layouts[] = {
    /* symbol     arrange function */
    { "[]=",    tile },    /* first entry is default */
    { "><>",    NULL },    /* no layout function means floating behavior */
    { "[M]",    monocle },
};

/* key definitions */
#define MODKEY Mod1Mask
#define TAGKEYS(KEY,TAG) \
    { MODKEY,                 KEY,    view,         {.ui = 1 << TAG} }, \
    { MODKEY|ControlMask,         KEY,    toggleview,     {.ui = 1 << TAG} }, \
    { MODKEY|ShiftMask,         KEY,    tag,        {.ui = 1 << TAG} }, \
    { MODKEY|ControlMask|ShiftMask, KEY,    toggletag,    {.ui = 1 << TAG} },

/* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }

/* commands */
static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_bg, "-nf", col_normfg, "-sb", col_selbg, "-sf", col_selfg, NULL };
static const char *termcmd[]  = { "st", NULL };

static Key keys[] = {
    /* modifier               key      function      argument */
    { MODKEY,                 XK_p,    spawn,        {.v = dmenucmd } },
    { MODKEY|ShiftMask,         XK_Return, spawn,        {.v = termcmd } },
    { MODKEY,                 XK_b,    togglebar,    {0} },
    { MODKEY,                 XK_j,    focusstack,     {.i = +1 } },
    { MODKEY,                 XK_k,    focusstack,     {.i = -1 } },
    { MODKEY,                 XK_i,    incnmaster,     {.i = +1 } },
    { MODKEY,                 XK_d,    incnmaster,     {.i = -1 } },
    { MODKEY,                 XK_h,    setmfact,     {.f = -0.05} },
    { MODKEY,                 XK_l,    setmfact,     {.f = +0.05} },
    { MODKEY,                 XK_Return, zoom,         {0} },
    { MODKEY,                 XK_Tab,    view,         {0} },
    { MODKEY|ShiftMask,         XK_c,    killclient,     {0} },
    { MODKEY,                 XK_t,    setlayout,    {.v = &layouts[0]} },
    { MODKEY,                 XK_f,    setlayout,    {.v = &layouts[1]} },
    { MODKEY,                 XK_m,    setlayout,    {.v = &layouts[2]} },
    { MODKEY,                 XK_space,  setlayout,    {0} },
    { MODKEY|ShiftMask,         XK_space,  togglefloating, {0} },
    { MODKEY,                 XK_agrave,    view,    {.ui = ~0 } },
    { MODKEY|ShiftMask,         XK_agrave,    tag,     {.ui = ~0 } },
    { MODKEY,                 XK_comma,  focusmon,     {.i = -1 } },
    { MODKEY,                 XK_semicolon, focusmon,     {.i = +1 } },
    { MODKEY|ShiftMask,         XK_comma,  tagmon,       {.i = -1 } },
    { MODKEY|ShiftMask,         XK_semicolon, tagmon,    {.i = +1 } },
    TAGKEYS(                XK_ampersand,               0)
    TAGKEYS(                XK_eacute,                1)
    TAGKEYS(                XK_quotedbl,                2)
    TAGKEYS(                XK_apostrophe,              3)
    TAGKEYS(                XK_parenleft,               4)
    TAGKEYS(                XK_minus,                 5)
    TAGKEYS(                XK_egrave,                6)
    TAGKEYS(                XK_underscore,              7)
    TAGKEYS(                XK_ccedilla,                8)
    { MODKEY,                 XK_a,    spawn,        SHCMD("~/.dmenu/actions") },
    { MODKEY,                 XK_s,    spawn,        SHCMD("~/.dmenu/sch") },
    { MODKEY,                 XK_x,    spawn,        SHCMD("~/.dmenu/dmlaunch") },
    { MODKEY,                 XK_w,    spawn,        SHCMD("web") },
    { MODKEY,                 XK_z,    spawn,        SHCMD("~/.dmenu/dmpc") },
    { MODKEY,                 XK_n,    spawn,        SHCMD("mpc next") },
    { MODKEY,                 XK_v,    spawn,        SHCMD("mpc toggle") },
    { MODKEY,                 XK_equal , spawn,        SHCMD("mixerctl outputs.master=+10") },
    { MODKEY,                 XK_parenright,    spawn,   SHCMD("mixerctl outputs.master=-10") },
    { MODKEY|ShiftMask,         XK_q,    quit,         {0} },
};

/* button definitions */
/* click can be ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
static Button buttons[] = {
/* click            event mask    button        function      argument */
{ ClkLtSymbol,        0,          Button1,      setlayout,    {0} },
{ ClkLtSymbol,        0,          Button3,      setlayout,    {.v = &layouts[2]} },
{ ClkWinTitle,        0,          Button2,      killclient,     {0} },
{ ClkWinTitle,        0,          Button4,      focusstack,     {.i = +1 } },
{ ClkWinTitle,        0,          Button5,      focusstack,     {.i = -1 } },
{ ClkStatusText,      0,          Button2,      spawn,        {.v = termcmd } },
{ ClkClientWin,       MODKEY,       Button1,      movemouse,    {0} },
{ ClkClientWin,       MODKEY,       Button2,      togglefloating, {0} },
{ ClkClientWin,       MODKEY,       Button3,      resizemouse,    {0} },
{ ClkTagBar,        0,          Button1,      view,         {0} },
{ ClkTagBar,        0,          Button3,      toggleview,     {0} },
{ ClkTagBar,        MODKEY,       Button1,      tag,        {0} },
{ ClkTagBar,        MODKEY,       Button3,      toggletag,    {0} },
{ ClkStatusText,      0,          Button3,      spawn,        SHCMD("tkmenu") },
{ ClkRootWin,         0,          Button3,      spawn,        SHCMD("tkmenu") },
{ ClkStatusText,      0,          Button4,      spawn,        SHCMD("mixerctl outputs.master=+10") },
{ ClkStatusText,      0,          Button5,      spawn,        SHCMD("mixerctl outputs.master=-10") },
};

Avec cette configuration, vous aurez quelques raccourcis claviers supplémentaire pour gérer le volume sonore (mixerctl), la possibilité de lancer quelques scripts enregistrés dans un dossier .dmenu.

Avec la souris, le clic-milieu sur le titre d'une fenêtre la ferme. Un clic droit sur la barre de status lance l'application “tkmenu”. Le scroll de la souris sur le titre d'une fenêtre permet de changer la fenêtre sélectionnée : pratique en mode maximisé. Le scroll de la souris sur la barre de status permet de modifier le volume sonore.

appliquer un patch

Vous pouvez ajouter toutes sortes de fonctionnalités à dwm, en ajoutant des patches. Le site officiel regorge d’informations à ce sujet, et la configuration de chaque patch est expliqué. De façon générale, pour appliquer un patch, il faudra faire ceci dans le dossier de dwm pour la version tarball :

patch -p1 < chemin/vers/le/patch.diff

Pour une version issue du dépôt git :

git apply /chemin/vers/le/patch.diff

Mais comment tu fais pour avoir le code des touches, ou les évènements de la souris?

La réponse est simple. Lancez la commande xev en console. Un fenêtre blanche apparaît. Lorsque vous mettez la souris dedans, vous voyez apparaître tout un tas de message dans le terminal : ce sont les évènements qu'il détecte.

Pour avoir le code d'une touche, tapez sur cette touche, vous verrez apparaître un message de ce type (appui sur le 0 du clavier, soit le à):

Vous pouvez donc récupérer le code de la touche entre parenthèses, ici, ce sera XK_agrave.

Notez que cela vaut aussi pour la souris, afin de récupérer le numéro du bouton cliqué, ou tout autre évènement.

Changer la touche méta par défaut

Par défaut, c'est la touche Alt (ou Mod1) qui sert à changer de fenêtres, lancer dmenu, cacher la barre… etc. Par souci de compatibilité avec d'autres logiciels, ou par habitude, il est possible de choisir n'importe quelle autre touche. Par exemple, on peut utiliser la touche “windows” (Mod4), qui ne sert jamais. Elle est reconnue par le code Mod4Mask. Alors, dans le config.h, on remplace la définition de MODKEY par la valeur souhaitée :

/* key definitions */
#define MODKEY Mod4Mask

Règles pour les fenêtres

Il est bon de lire la documentation officielle.

Ici, vous allez apprendre à définir un comportement particulier selon la fenêtre. Par exemple, si je veux assigner l'étiquette “7” au programme gajim, il est possible de définir cette règle :

static const Rule rules[] = {
/* class instance title tags mask isfloating monitor */
{ "Gajim", NULL, NULL, 1 << 6, False, -1 },
};

Trouver class et title d'une fenêtre

Pour récupérer les informations sur une fenêtre, tapez xprop dans une console. Votre curseur va changer de forme. Cliquez alors sur la fenêtre voulue. Vous verrez apparaître dans la console les informations sur la classe, le titre de la fenêtre…

Voici ce que par exemple vous obtiendrez :

WM_CLASS(STRING) = "Navigator", "Iceweasel"
WM_ICON_NAME(STRING) = "Rendez-vous sur Arrakis : dwm - Iceweasel"
_NET_WM_ICON_NAME(UTF8_STRING) = "Rendez-vous sur Arrakis : dwm -
Iceweasel"
WM_NAME(STRING) = "Rendez-vous sur Arrakis : dwm - Iceweasel"
_NET_WM_NAME(UTF8_STRING) = "Rendez-vous sur Arrakis : dwm - Iceweasel"

WM_CLASS donne la “class” de la fenêtre, et WM_NAME le “name” de la fenêtre.

Notez que l'on peut donner à un terminal un titre précis. Par exemple, avec urxvt, c'est avec l'option “-T”. Avec st, c'est comme xterm, avec “-t”. On peut donc envoyer un terminal à l'étiquette 8, avec cette règle

{ “NULL”, NULL, “MonTerminal”, 1 << 7, False, -1 },

Par contre, il faudra le lancer ainsi : st -t "MonTerminal". Ça peut être utile dans des scripts…

Fenêtre flottante

Vous l’aurez compris, si vous voulez que la fenêtre flotte, il faudra mettre True dans la colonne isfloating.

Fenêtre à une étiquette précise

Pour désigner le tag où l’on veut automatiquement assigner à une fenêtre, c’est un peu plus complexe.

Changer la police

La police peut maintenant être n'importe laquelle depuis la version 6.1. Il faut modifier les lignes suivantes du fichier config.h :

static const char *fonts[]        = { "Hack:size=9" };
static const char dmenufont[]     = "Hack:size=9";

Bien sûr, mettez ce que vous préférez à la place de Hack.

Définir vos raccourcis clavier

Il est bien entendu possible d'exécuter n'importe quelle commande via un raccourci clavier.

Méthode simple

Dwm propose déja une fonction toute prête pour lancer n'importe quelle commande : SHCMD.

/* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }

Alors, vous pouvez définir n'importe quel raccourci clavier dans la section static Key keys[] = {.

Pour passer à la chanson suivante de la liste de lecture de mpd, je lance la commande mpc next. Pour l'associer au raccourci ctrl+flèche_droite J'ajoute à la section Key keys :

{ ControlMask,    XK_Right,  spawn,    SHCMD("mpc next") },

Autre exemple. J’ai envie de lancer rox lorsque j’appuie sur Mod4+Mod1+h, et couper le son lorsque j’appuie sur la touche multimédia de mon ordinateur. J'écrirais alors ces lignes suivantes :

{ 0,            0x1008ff12,    spawn,        SHCMD("amixer -q set Master toggle") },
{ MODKEY|Mod1Mask,  XK_h,        spawn,        SHCMD("rox") },

Hé, mais c'est quoi ça, Mod4 et Mod1 ??? Ah oui, j'allais oublier. Mod4 désigne la touche Méta, souvent la touche “windows” située entre Fn et alt. Mod1 quand à lui, désigne la touche Alt.

Autre méthode

Il faut d'abord définir la commande à éxécuter, puis ensuite, on définira le raccourci.

Les commandes sont définies à cet endroit du config.h, il y en a déja deux :

/* commands */
static const char *dmenucmd[] = { "dmenu_run", "-fn", font, "-nb", normbgcolor, "-nf", normfgcolor, "-sb", selbgcolor, "-sf", selfgcolor, NULL };
static const char *termcmd[] = { "uxterm", NULL };

La première ligne sert à lancer dmenu avec les couleurs de dwm. C’est en fait ce qui se passe lorsque vous tapez le raccourci Mod+P . dmenu est inclus dans le paquet dwm-tools ou suckless-tools sous debian.

La deuxième ligne lance un terminal, c’est ce qui se passe lorsque vous faîtes Mod+Shift+Entrée. Vous pouvez modifier la commande uxterm, par votre terminal favori (xterm, urxvt, xfce4-terminal, lxterminal…).

On va ajouter en-dessous de ces lignes nos propres fonctions!

Par exemple, je voudrais ajouter deux raccourcis : l’un pour lancer gajim, l’autre pour couper/activer le son. Il faudra définir deux fonctions sur le même modèle que les précédentes. Donnez leur le nom que vous voulez, du moment que vous vous y retrouvez.

static const char *gajim[] = { "gajim", NULL };
static const char *muteson[] = { "amixer", "-q", "set", "Master", "toggle", NULL };

Note : Remarquez que lorsque une commande contient des espaces, chaque argument doit être entre guillemets suivi d’une virgule.

Les commandes finissent toujours par NULL.

Bien, maintenant que nos fonctions sont définies, on va les associer à un raccourci clavier. Voici ce que ça donnera, dans la section des raccourcis claviers :

static Key keys[] = {
/*il y a d’autres choses avant….*/
{ MODKEY|Mod1, XK_i, spawn, {.v = gajim } },
{ 0, 0x1008ff12, spawn, {.v = muteson } },

Quelques explications:

Et pour la souris?

Bien sûr, on peut reprendre le même schéma que pour le clavier. En fait, j'utilise pour ma part, une action personnalisée pour qu'un clic-droit sur la barre de status ou le fond d'écran, m'affiche un menu.

Voici les étapes à suivre :

On peut aussi modifier les raccourcis déja existants. Si l'on souhaite que lorsqu'on clic-milieu sur le titre d’une fenêtre, ça la ferme :

static Button buttons[] = {
/* click event mask button function argument */
{ ClkWinTitle, 0, Button2, killclient, {0} },
};

Autre exemple, ici, faire tourner la molette de la souris sur le titre des fenêtres permet de les “faire tourner”. Très pratique lorsqu'on utilise le mode maximisé afin de changer facilement de fenêtres :

{ ClkWinTitle,        0,          Button4,      focusstack,     {.i = +1 } },
{ ClkWinTitle,        0,          Button5,      focusstack,     {.i = -1 } },

Voila, rien de bien compliqué en définitive. On définit d’abord la commande à éxécuter, en lui donnant un nom de fonction au choix. Ensuite, on peut lancer cette commande avec spawn, suivi du nom de la fonction.

Changer le volume sonore avec la molette de la souris

Sur le même modèle que le paragraphe précédent, on peut augmenter/diminuer le volume sonore lorsque l'on fait rouler la molette de la souris sur la barre de status.

Premièrement, lancez xev, et faîtes rouler la molette dedans sa fenêtre pour vois à quel bouton de la souris ça correspond (cela s'affiche dans la console). Chez moi, c'est les boutons 4 et 5.

Et enfin, tout en bas, dans la section de la souris, après static Button buttons[] = { on indique les raccourcis, c'est à dire la molette sur la barre de status, avec la commande permettant de changer le son lancée avec SHCMD :

{ ClkStatusText,      0,          Button4,      spawn,    SHCMD("amixer -q set Master 2dB+ unmute") },
{ ClkStatusText,      0,          Button5,      spawn,    SHCMD("amixer -q set Master 2dB- unmute") },

Dans cet exemple, c'est amixer qui permet de modifier le volume sonore sous Linux si vous n'utilisez pas pulseaudio. Une autre commande nommée mixerctl est disponible sous OpenBSD.

Modifier la configuration pour les claviers français

Les touches “chiffre”

Malheureusement, dwm est configuré par défaut pour les claviers anglais. Les chiffres au dessus des lettres de votre clavier ne seront pas lus comme tels, mais comme étant un “&” pour le “1”, un “é” pour le “2”, un ““” pour le “3”… On va donc remédier à tout ça. Le code de nos touches se récupère très facilement avec xev. Il faut lancer xev en console, taper sur une touche, et lire ce qui est retourné.

Remplaçons cette portion :

TAGKEYS(                XK_1,                0)
TAGKEYS(                XK_2,                1)
TAGKEYS(                XK_3,                2)
TAGKEYS(                XK_4,                3)
TAGKEYS(                XK_5,                4)
TAGKEYS(                XK_6,                5)
TAGKEYS(                XK_7,                6)
TAGKEYS(                XK_8,                7)
TAGKEYS(                XK_9,                8)

Par :

TAGKEYS(                XK_ampersand,               0)
TAGKEYS(                XK_eacute,                1)
TAGKEYS(                XK_quotedbl,                2)
TAGKEYS(                XK_apostrophe,              3)
TAGKEYS(                XK_parenleft,               4)
TAGKEYS(                XK_minus,                 5)
TAGKEYS(                XK_egrave,                6)
TAGKEYS(                XK_underscore,              7)
TAGKEYS(                XK_ccedilla,                8)

Ainsi que :

{ MODKEY,                 XK_0,    view,         {.ui = ~0 } },
{ MODKEY|ShiftMask,         XK_0,    tag,        {.ui = ~0 } },

Par :

{ MODKEY,                 XK_agrave,    view,         {.ui = ~0 } },
{ MODKEY|ShiftMask,         XK_agrave,    tag,        {.ui = ~0 } },

Pour changer d'écran

Il faut françiser la touche “.” pour nos claviers. Donc, remplacer dans le config.h “period” par “semicolon”, car pour nous, c’est le point-virgule :

{ MODKEY, XK_comma, focusmon, {.i = -1 } },
{ MODKEY, XK_semicolon, focusmon, {.i = +1 } },
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
{ MODKEY|ShiftMask, XK_semicolon, tagmon, {.i = +1 } },

Personnaliser la barre d'état

Il est possible d’afficher ce que l’on veut dans la barre d’état. Cela se définit juste avant le lancement de dwm, donc selon les cas dans votre ~/.xsession ou autre. Dwm lira ce qui est passé en argument à la commande xsetroot. Cela donnera quelque chose comme ça :

while true; do
    xsetroot -name "$(date +"%F %R" )"
    sleep 5 # Update time every 5 seconds
done &
dwm

Ça affiche la date et l'heure, tout simplement.

Notez que l'on peut faire de même avec des scripts plus compliqués ou en les écrivant en python, en C, en ce que vous voulez… Par exemple, ça donne :

aperçu de dwmstatus

En fait, vous pouvez afficher ce que vous souhaitez, du moment que vous passez ce texte à travers xsetroot.

Il existe même un projet de lié à suckless.org pour faire ça en suivant la philosophie suckless : slstatus.

Avec conky-cli

Encore plus simple. Conky-cli indique dans la console les information que conky affiche d'habitude en graphique. Pour afficher la sortie de conky-cli dans la barre de dwm, modifiez votre ~/.xinitrc pour qu'il ressemble à ça :

conky | while read -r; do xsetroot -name "$REPLY"; done

Et le conkyrc :

update_interval 1
TEXT
${if_mpd_playing} $mpd_smart $mpd_elapsed/$mpd_length | ${endif}Cpu: $cpu% | Mem: ${memperc}% | Swap: ${swapperc}% | Bat: ${battery_short} | /: ${fs_free_perc /}% /home: ${fs_free_perc /home}% | M:${execi 300 ~/.Scripts/mailinfo.py} | Vol: ${mixer}% | D: ${downspeed wlan0}/s U:${upspeed wlan0}/s | ${time %d/%m/%y %H:%M }

Résultat en image :

aperçu du status dwm avec conky

Avec slstatus

Il est possible de faire exactement la même chose, mais entièrement en C. Je vous laisse imaginer le gain en performance!

Des fonctions toutes prêtes sont disponibles. Consultez le site : https://git.suckless.org/slstatus/file/README.html.

Voici à quoi ressemble mon fichier de configuration de slstatus :

static const struct arg args[] = {
    /* function format        argument */
    { run_command, "%s ",         "~/bin/mpd-status" },
    { run_command, "[%s] ",         "~/bin/nbmail.sh" },
    { datetime, "[%s]",         "%F %T" },
};

Les scripts mpd-status et nbmail.sh servent à afficher la chanson jouée par mpd et le nombre de nouveaux mails. Ils sont disponibles sur mon dépôt.

Bonus pour les utilisateurs d'OpenBSD

Si vous utilisez OpenBSD (vous avez bien raison ;)), sachez que dwm profite de “pledge” par défaut :)

Conclusion

Il y a un programme essentiel à utiliser dans dwm : j’ai nommé dmenu. Vous pourrez au fur et à mesure lire des exemples d’utilisation que je posterai ici, dans la catégorie dmenu.

N'hésitez pas à me contacter pour toute précision, commentaire, ou astuces à ajouter.