XMonad

Cuando recién comencé a usar Linux, no tenía ninguna preferencia por algún administrador de ventanas. Por supuesto, en ese entonces solamente conocía lo que venía por default (Gnome o KDE), pero las distribuciones que usaba por lo general dejaban a Gnome por default. Luego conocí XFCE, fluxbox y algunas otras, ampliando así la selección.

Por influencia de un amigo, comencé a usar administradores de ventanas por mosaico. Estos se basan en dividir la pantalla en partes que son ocupadas por los programas; las partes no se enciman, sino que la pantalla se va “partiendo” cada que un programa se inicia en modo gráfico. Administradores como Gnome, KDE, XFCE, el mismo Windows de Microsoft y OSX de Apple usan la pantalla como una pila de coordenadas, en donde se van poniendo las ventanas que se requieran. Creo que sobra decir que, a diferencia de Windows y OSX, el entorno gráfico de Linux (X) es simplemente otro programa (no un sistema operativo), por lo que no hay un administrador de ventanas “preferido” para X, y en una misma computadora se pueden tener tantos como se quiera. Por ejemplo, mi computadora de escritorio tiene Gnome y dwm; mi laptop “inmortal” (que ya no está conmigo) tenía XMonad y XFCE; en el trabajo uso XMonad y Gnome.

Primero comencé con Ion2. Sinceramente no entendía todavía los beneficios de un administrador de ventanas por mosaico, y ciertamente la transición de Gnome o similares a uno de ellos no es instantánea y toma un poco de tiempo acostumbrarse. Terminé por casi no usarlo, pero despertó mi curiosidad.

Luego seguí con dwm, y a éste fue el primero que le saqué provecho. Quizá sea gracias al lado geek, pero me atrapó el hecho de que, para personalizarlo, tienes que meterte a editar directamente el código fuente del archivo de configuración (el cual está en C), y puedes redirigir la salida de otros programas para mostrar diferentes estatus de la computadora (como fecha y hora, memoria usada, título de la “ventana” actual, etc.). Duré un tiempo con dwm, pero poco a poco fue cayendo de mi gracia debido a que la documentación era muy poca, y la comunidad era muy elitista, por lo que no respondían a cualquier pregunta, y sin documentación, muchas veces había que adivinar qué hacía cada función. Al final, lo dejé porque no pude echar a andarlo con soporte para UTF-8, y como el idioma japonés es el que tengo por default en todas las computadoras que uso, sí era algo que me importaba. Dicho sea de paso, desde que llegué a Japón no me hago la idea de tener Linux sin soporte para japonés desde que lo instalo.

La idea de dwm era buena, pero la falta de ayuda y documentación, aunado con lo del UTF-8, hicieron que buscara más alternativas. Fue entonces cuando el mismo amigo que me presentó Ion2 me dijo sobre XMonad.

XMonad es otro administrador de ventanas por mosaico. Originalmente es un clon de dwm, pero está escrito en Haskell, un lenguaje de programación funcional; además, es más amigable y más “extensible” que el original.

El concepto es el mismo: la pantalla va siendo dividida en mosaicos como vaya siendo necesario. La forma en que se divide se decide dependiendo del layout que haya sido escogido. Se pueden tener además escritorios virtuales, y cada uno puede tener un layout diferente. Cada layout define además un área maestra en la pantalla donde se pone cada nueva ventana que aparece; el área maestra es normalmente más grande ya que se basa en la idea de que en esta ventana en donde se trabaja o se realiza la actividad más importante. Lógicamente se puede cambiar libremente la ventana que ocupa esa área.

Como ejemplos de layout, aquí 2 imágenes.

Éste es el layout Tall. El área maestra es la izquierda de la pantalla.

Aquí el layout Circle. El área maestra en el centro.

En XMonad (al igual que en otros administradores de ventanas por mosaico), la idea es realizar la mayoría de tareas con el teclado. No hay menúes estilo GNOME o KDE para abrir programas o cambiar configuraciones, por lo que es necesario conocer cómo se configura todo de forma manual. Y cuando digo “todo”, es “todo”, desde  cambiar el fondo de pantalla hasta el brillo o el volumen, tareas que en otros entornos fácilmente se pueden ejecutar con unos cuantos clicks.

Cabe mencionar que es posible cambiar una ventana a modo “float”, en el cual se respeta su tamaño original y se maneja, en términos de coordenadas, como una ventana “normal”, aunque no tiene los íconos de cerrar, minimizar ni maximizar.

GIMP en modo float. Noten como la ventana en el área maestra del layout circle no es afectada.

Como XMonad no incluye ni menúes ni ejecutores de programas, lo que se recomienda es instalar al mismo tiempo programas que faciliten esas tareas. La opción más común es dmenu para ejecutar programas, y dzen2 o xmobar para mostrar mensajes y crear barras informativas. En mi caso, uso dzen2 y una serie de scripts para crear los mensajes que se ven en la parte inferior derecha de las imágenes arriba mostradas. Herramientas como conky permiten, en combinación con xmobar o dzen2, mostrar información sobre diferentes recursos (memoria, cpu, red, etc.) de forma sencilla, razón por la cual muchas personas lo prefieren.

XMonad se configura por medio de un archivo en haskell llamado xmonad.hs. Es aquí donde se hacen los cambios y donde se personaliza todo.

Éste es mi xmonad.hs en la laptop:

import XMonad
import XMonad.Hooks.DynamicLog
import XMonad.Hooks.ManageDocks
import XMonad.Util.Run(spawnPipe)
import XMonad.Util.EZConfig(additionalKeys)
import System.IO
import XMonad.Hooks.SetWMName
import XMonad.Hooks.ManageHelpers
import XMonad.Layout.Dishes
import XMonad.Layout.DragPane
import XMonad.Layout.Grid
import XMonad.Layout.NoBorders
import XMonad.Layout.ThreeColumns
import XMonad.Layout.Circle

myManageHook = composeAll
	     [ className =? "Emacs" --> doShift "emacs"
	     , className =? "Firefox" --> doShift "web"
	     , className =? "Thunderbird" --> doShift "mail"
	     , className =? "Eclipse" --> doShift "dev"
	     , className =? "K3b" --> doShift "apps"
	     , className =? "Xpdf" --> doShift "pdf"
             , className =? "Acroread" --> doShift "pdf"
             , className =? "OpenOffice.org 3.2" --> doShift "apps"
             , className =? "Gcalctool" --> doFloat
             , className =? "URxvt" --> doShift "term"
             , className =? "Liferea" --> doShift "misc"
             , className =? "Google-chrome" --> doShift "www"
             , className =? "MPlayer" --> doFloat <+> doShift "vids"
             , className =? "Skype" --> doFloat <+> doShift "misc"
             , className =? "Gimp" --> doShift "apps"
             , className =? "Gtk-recordMyDesktop" --> doFloat
             , className =? "Empathy" --> doFloat <+> doShift "misc"
             , isFullscreen --> doFullFloat
             , manageDocks
	     ]

main = do
  dzproc <- spawnPipe myStatusBar
  dateproc <- spawnPipe myMiscBar
  cpuinfoproc <- spawnPipe myPcInfoBar

  xmonad $ defaultConfig {
  	       terminal = "urxvt -fg white -bg black -tr -sh 5 -fn 'xft:Dejavu Sans Mono:pixelsize=10' +sb"
	     , modMask = mod4Mask
             , startupHook = setWMName "LG3D"
	     , manageHook = manageDocks <+> myManageHook <+> manageHook defaultConfig
--	     , layoutHook = avoidStruts $ layoutHook defaultConfig
,layoutHook = myLayout
             , logHook = dynamicLogWithPP $ myDzenPP dzproc
             , workspaces = ["term","www","dev","emacs","mail","apps","pdf","vids","misc"]
             } `additionalKeys`
	     [ ((controlMask, xK_Print), spawn "sleep 0.2; scrot -s")
	     , ((0, xK_Print), spawn "scrot")
	     , ((mod1Mask, xK_Shift_L), spawn "chkblayout")
	     , ((mod4Mask .|. shiftMask, xK_w), spawn "setwallpaper")
             , ((mod4Mask, xK_Up), spawn "amixer set Master 1%+")
             , ((mod4Mask, xK_Down), spawn "amixer set Master 1%-")
             , ((mod4Mask, xK_m), spawn "sonoff")
	     ]

myStatusBar = "/usr/bin/dzen2 -x 0 -y 756 -h 12 -w 1030 -ta l -fn -sazanami-*-*-*-*-*-11-10-0-0-p-*-*-*"

myMiscBar = "/usr/local/bin/miscbarinfo"

myPcInfoBar = "/usr/local/bin/pcinfobar"

myDzenPP h = dzenPP
             { ppOutput = hPutStrLn h
             , ppCurrent = dzenColor "green" ""
             , ppHidden = dzenColor "#5f7962" ""
             , ppVisible = dzenColor "#f4a460" ""
             , ppUrgent = dzenColor "#cc0000" "" . wrap "*" "*"
             , ppSep = " | "
             , ppWsSep = "  "
             , ppLayout = dzenColor "#5CBAF5" "" .
                          (\x -> case x of
                                   "Tall" -> icon "tall.xbm"
                                   "Mirror Tall" -> icon "mirrortall.xbm"
                                   "ThreeCol" -> icon "threecolumns.xbm"
                                   "Dishes 2 (1 % 6)" -> icon "dishes.xbm"
                                   "Circle" -> icon "circle.xbm"
                                   "DragPane  Horizontal 0.1 0.5" -> icon "dragpanehorizontal.xbm"
                                   "DragPane  Vertical 0.1 0.5" -> icon "dragpanevertical.xbm"
                                   "Grid" -> icon "grid.xbm"
                                   "Full" -> icon "full.xbm"
                           )
             , ppTitle = dzenColor "white" "" . shorten 150
             }
             where
               icon h = "^i(/home/mmedina/Images/Icons/layouticons/" ++ h ++ ")"

myLayout = avoidStruts (smartBorders (tiled ||| Mirror tiled ||| ThreeCol 1 (3/100) (1/2) ||| Dishes 2 (1/6) ||| Circle ||| dragPane Horizontal 0.1 0.5 ||| dragPane Vertical 0.1 0.5 ||| Grid ||| noBorders Full))
  where
     -- default tiling algorithm partitions the screen into two panes
     tiled   = Tall nmaster delta ratio
--     tiled   = ResizableTall nmaster delta ratio []

     -- The default number of windows in the master pane
     nmaster = 1

     -- Default proportion of screen occupied by master pane
     ratio   = 3/5

     -- Percent of screen to increment by when resizing panes
     delta   = 3/100

Explicarlo todo tomaría mucho tiempo, y existen sitios en donde hay tutoriales muy buenos para entender qué hace cada parte de este código. Lo que sí voy a mencionar aquí es que tengo la tecla de control de XMonad mapeada a la tecla de Windows (mod4Mask), y que con ciertas combinaciones de teclas cambio el volumen (o pongo el mute), configuro la imagen de fondo de escritorio o cambio el layout del teclado entre japonés y español. Uso además una serie de imágenes para mostrar el layout del escritorio actual (normalmente sale el nombre, pero en el código lo capturo y lo cambio por imágenes de 16×16). Además, se puede observar que uso 2 scripts, que son los que mencionaba arriba para crear la parte inferior derecha del escritorio. A continuación pongo el código fuente:

miscbarinfo:

#!/bin/bash

ICONS=/home/mmedina/Images/Icons/xbm8x8
FONT=-sazanami-*-*-*-*-*-11-10-0-0-p-*-*-*

while true; do
      now=`date +"%Y/%m/%d (%a) %R"`
      sound=`amixer get Master | grep Mono: | cut -d" " -f8 | tr -d []`

      if [ $sound == "on" ]; then
            vol=`amixer get Master | grep Mono: | cut -d" " -f6 | tr -d []`
            spkicon=$ICONS/spkr_01.xbm
      else
            vol="MT"
            spkicon=$ICONS/spkr_02.xbm
      fi

      echo "^i(${spkicon}) $vol ^fg(yellow)^i(${ICONS}/clock.xbm) $now"
      sleep 2
done | dzen2 -x 1184 -y 756 -w 182 -h 12 -ta r -fg white -fn ${FONT}

pcinfobar:

#!/bin/bash

ICONS=/home/mmedina/Images/Icons/xbm8x8
FONT=-sazanami-*-*-*-*-*-11-10-0-0-p-*-*-*

while true; do
      memtot=`free -t | grep Mem | tr -s " " | cut -d" " -f2`
      memused=`free -t | grep Mem | tr -s " " | cut -d" " -f3`
      memfree=`gcalctool -s "(${memused}/${memtot})*100" | cut -d"." -f1`
      acadapt=`acpi -a | cut -d":" -f2 | tr -d " "`
      kbly=`setxkbmap -print | grep xkb_symbols | awk '{print $4}' | cut -d"+" -f2`
      kbicon=$ICONS/keyboard8x8.xbm

      batorac=`acpi -b | cut -d"," -f2 | tr -d " %"`
      if [ $acadapt != "on-line" ]; then
	  #batorac=`acpi -b | cut -d"," -f2 | tr -d " %"`
	  if [ $batorac -gt 40 ]; then
	      batimg=$ICONS/bat_full_01.xbm
	      batcolor=#c6f9ff
	  elif [ $batorac -gt 10 ] && [ $batorac -lt 41 ]; then
	      batimg=$ICONS/bat_low_01.xbm
	      batcolor=#f54312
	  else
	      batimg=$ICONS/bat_empty_01.xbm
	      batcolor=red
	  fi
	  #batorac=${batorac}%
      else
	 # batorac="AC"
	  batimg=$ICONS/ac_01.xbm
	  batcolor=#c6f9ff
      fi

      batorac=${batorac}%

      if [ $memfree -lt 41 ]; then
	  COLOR=#90b7de
      elif [ $memfree -gt 40 ] && [ $memfree -lt 85 ]; then
	  COLOR=#e9a11a
      else
	  COLOR=red
      fi

      echo " ^i(${kbicon}) $kbly ^fg($COLOR)^i(${ICONS}/mem.xbm) $memfree% ^fg($batcolor)^i($batimg) $batorac"

      sleep 2
done | dzen2 -x 1031 -y 756 -w 152 -h 12 -ta r -fn $FONT

Como podrán darse cuenta, hay mucho por mejorar en estos scripts. Podría (y de hecho debo) agregar más colores al nivel de la batería, pero ésas son nimiedades.

Trabajar con XMonad (y para el caso, con este tipo de administradores de ventanas) es un estilo. Hay gente que prefiere tener ventanas estilo Windows y trabaja a la perfección en ambientes como GNOME o KDE. Incluso se puede usar XMonad en GNOME con un sencillo cambio al momento de iniciar el segundo. En lo personal, poderse mover rápidamente por los escritorios virtuales y las ventanas usando solamente el teclado es una característica muy buena (y tener integrado xmms2 en XMonad hace que escuchar música sea realmente sencillo). Si tienen oportunidad y tiempo, les recomiendo que lo instalen y lo revisen. Recuerden que siempre pueden cambiar el administrador de ventanas al iniciar sesión (ya sea con gdm o bien editando el .xsession si van a iniciar desde modo texto). Aprender un poco de Haskell es necesario para poder personalizar XMonad, pero para probarlo no hace falta 😀

Recomandado para gente que gusta de realizar sus tareas con el teclado y quienes saben usar bien una terminal 🙂