Días atrás hablamos sobre DWM y quedó pendiente explicar cómo colorear la barra de estado. En el artículo de hoy veremos cómo aplicar el parche Simple StatusColor en DWM 6.1, que escribí hace poco.



Para aquel que quiera simplemente aplicar el parche y empezar a usarlo, debe leer únicamente la siguiente sección. Aquellos que estén interesados en los detalles técnicos pueden leer el artículo completo.

Aplicar y usar el parche

El primer paso es situarnos en el directorio con el código de DWM y descargar el parche:

wget https://raw.githubusercontent.com/algui91/myDWM/master/patches/dwm-6.1-simplestatuscolor.diff

Aplicamos el parche:

git apply dwm-6.1-simplestatuscolor.diff

Compilamos e instalamos:

$ sudo make clean install

Crear una barra de estado con dwmstatus

Éste parche solo funciona con la aplicación dwmstatus, para descargarlo e instalarlo basta con hacer:

$ git clone git://git.suckless.org/dwmstatus
$ sudo make clean install
$ dwmstatus

Con esto tendremos dwmstatus ejecutándose y mostrando información.

Coloreando la barra de estado

Para darle color, basta con modificar el código de dwmstatus e indicar qué colores usar. Por ejemplo, en el fichero dwmstatus.c la línea que formatea el estado es la siguiente:

status = smprintf("L:%s A:%s U:%s %s", avgs, tmar, tmutc, tmbln);

Por defecto el parche tiene 7 colores, para indicar el color a usar se debe escribir el caracter \xCL al final del texto a colorear, donde CL es un dígito del 01 al 07. Por ejemplo, usando los tres primeros colores:

status = smprintf("L:%s\x01 A:%s\x02 U:%s %s\x03", avgs, tmar, tmutc, tmbln);

Coloreará L:%s con el color 1, L:%s con el color 2 y ` U:%s %s` con el color 3. He aquí un ejemplo de la mía:

Dentro del código

Pasemos ahora a ver qué hace el código para ser capaz de colorear la barra de estado. Explicaremos paso a paso lo que hace cada trozo de código en cada fichero:

/* ##### config.def.h ##### */
 /* appearance */
#define NUMCOLORS 7
static const unsigned long colors[] = {
    0x54ebff,   // \x01
    0xdb6794,   // \x02
    0xef9c3a,   // \x03
    0xa4bc74,   // \x04
    0x99FF99,   // Good \x05
    0xFF6600,   // Warning \x06
    0xC63333,   // Caution \x07
};

Aquí simplemente estamos definiendo el número de colores que usaremos y su valor (En Hexadecimal).

/* ##### drw.c ##### */
#define TEXTW(X)                (drw_font_getexts_width(drw->font, X, strlen(X)) + drw->font->h)
// ......
void
drw_colored_st(Drw *drw, int x, int y, unsigned int w, unsigned int h, char text[][256], const unsigned long *color, const char *ptext) {
  char buf[256];
  int i, tx, ty, th, len, olen;
  Extnts tex;

  if(!drw || !drw->scheme)
    return;
  XSetForeground(drw->dpy, drw->gc, drw->scheme->bg->rgb);
  XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
  if(!text || !drw->font)
    return;
  olen = strlen(ptext);
  drw_font_getexts(drw->font, ptext, olen, &tex;);
  th = drw->font->ascent + drw->font->descent;
  ty = y + (h / 2) - (th / 2) + drw->font->ascent;
  tx = x + (h / 2);
  /* shorten text if necessary */
  for(len = MIN(olen, sizeof buf); len && (tex.w > w - tex.h || w < tex.h); len--)
    drw_font_getexts(drw->font, ptext, len, &tex;);
  if(!len)
    return;
  memcpy(buf, ptext, len);
  if(len < olen)
    for(i = len; i && i > len - 3; buf[--i] = '.');

  for (int k = 0; color[k]; k++) {
    XSetForeground(drw->dpy, drw->gc, color[k]);
    if (drw->font->set)
      XmbDrawString(drw->dpy, drw->drawable, drw->font->set, drw->gc, tx, ty,
                    text[k], strlen(text[k]));
    else
      XDrawString(drw->dpy, drw->drawable, drw->gc, tx, ty, text[k], strlen(text[k]));
    tx += TEXTW(text[k]) - TEXTW("\x0");
  }
}

drw.c es el encargado de dibujar los elementos en la pantalla. Aquí se introdujo un método similar a drw_text, que se encarga de dibujar el texto en la pantalla, pero con la posibilidad de colorear el texto además de dibujarlo. La macro TEXTW(X) devuelve el ancho que ocupa el texto X en píxeles con la fuente seleccionada.

La clave para colorear está en el último for, encargado de recorrer la lista de colores pendientes de dibujar junto con el texto correspondiente. La función XSetForeground de Xlib especifica el color, mientras que XmbDrawString se encarga de posicionar el texto recibido en las coordenadas correctas.

/* ##### dwm.c ##### */
void
parsestatus(char *text, unsigned long *color_queue, char tokens[][256]) {

  char delim[NUMCOLORS+1];

  /* Thanks to http://stackoverflow.com/a/24931903/1612432 */
  for (int i = 0; i < NUMCOLORS; ++i)
      delim[i] = i + 1;
  /* Terminates as string */
  delim[NUMCOLORS] = '\0';

  char *copy = strdup(text);
  char *res = strtok(copy, delim);
  if (!text[res - copy + strlen(res)]){
    // Status already parsed
    color_queue[0] = drw_clr_create(drw, normfgcolor)->rgb;
    strcpy(tokens[0], text);
    return;
  }

  char cleanBuf[strlen(text)];
  cleanBuf[0] = '\0';
  strcpy(tokens[0], res);
  strcat(cleanBuf, res);
  int i = 1;

  while (res) {
    /* Figure out what delimiter was used */
    // Thanks to http://stackoverflow.com/a/12460511/1612432
    int deli = text[res - copy + strlen(res)] - 1;
    color_queue[i-1] = colors[deli];
    res = strtok(0, delim);
    if (res){
      strcpy(tokens[i++], res);
      strcat(cleanBuf, res);
    }
  }
  free(copy);
  strncpy(text, cleanBuf, strlen(cleanBuf));
  text[strlen(cleanBuf)] = '\0';
  color_queue[i] = '\0';
}

Este básicamente, es el método principal, se encarga de parsear el estado para extrar qué colores corresponden a qué parte del texto. Almacena los colores en un array para recordar en qué orden aplicarlos.

Por último, solo queda usar los métodos creados, deben ir en el método drawbar, encargado de dibujar todo el recuadro que muestra información en DWM (etiquetas, ventana actual y barra de estado). Para ello se reemplaza el método drw_text por drw_colored_st cuando se vaya a pintar el estado. Habiéndolo parseado previamente. Ésta vez se muestra el diff, para que sea visto con mayor claridad.

+void
 drawbar(Monitor *m) {
   int x, xx, w;
   unsigned int i, occ = 0, urg = 0;
@@ -716,13 +760,14 @@ drawbar(Monitor *m) {
   x += w;
   xx = x;
   if(m == selmon) { /* status is only drawn on selected monitor */
+	  parsestatus(stext, color_queue, tokens);
     w = TEXTW(stext);
     x = m->ww - w;
     if(x < xx) {
       x = xx;
       w = m->ww - xx;
     }
-		drw_text(drw, x, 0, w, bh, stext, 0);
+		drw_colored_st(drw, x, 0, w, bh, tokens, color_queue, stext);
   }
   else
     x = m->ww;

Referencias

Todo el trabajo está en mi cuenta de Github, Tanto mi configuración actual de DWM como mi DWMstatus. En los siguientes enlaces.

myDWMstatus »» github.com/algui91/myDWMstatus
myDWM »» github.com/algui91/myDWM

Índice