commit d0eff3842a231f52840bcd8f5e14c8d4e89dd5f5
parent 47704a7f2321fe1fd35f856183196f0dc7a70e17
Author: fjbalon <fjbalon@templier.es>
Date: Thu, 25 Jun 2026 16:18:15 +0200
Añadido artículo sobre dmenu
Diffstat:
2 files changed, 335 insertions(+), 0 deletions(-)
diff --git a/dmenu/index.html b/dmenu/index.html
@@ -0,0 +1,330 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>Configuración propia del lanzador de aplicación dmenu</title>
+<link rel="icon" type="image/png" href="/favicon.png" />
+<link rel="stylesheet" type="text/css" href="../article.css" />
+</head>
+<body>
+<header>
+<p class="title">Configuración propia del lanzador de aplicación dmenu</p>
+<p class="date">25 de junio de 2026 • fjbalon</p>
+</header>
+<p>dmenu es un lanzador de aplicaciones, extremadamente ligero y rápido para el sistemas de ventanas X, diseñado originalmente para el gestor de ventanas dwm, de suckless. Es una herramienta que, para mí, casa matrimonialmente con dwm; no pudiendo ser verdaderamente usable uno sin el otro. Al menos, dwm no sería tan útil sin él.</p>
+<p>Puede encontrar mi propia versión del código en mi servidor de git: <a href="https://templier.es/git/dmenu/">/git/dmenu/</a>.</p>
+<p>La versión inicial es muy sencilla, al ser llamado mediante el comando <emb>dmenu</emb>, que por defecto dwm trae con acceso directo mediante teclas (<kbd>Mod1</kbd>+<kbd>p</kbd>, aunque yo personalmente lo he modificado a <kbd>Mod4</kbd>+<kbd>r</kbd>, me gusta más), ejecuta en la barra superior el lanzador, que conforme escribes irá indicando una lista de coincidencias, con una siempre seleccionada.</p>
+<p>Pero el código tiene algunas limitaciones para con mi forma de trabajar, por lo que he aplicado algunos cambios para que sea más ameno y útil su uso. Estos son los cambios que he realizado:</p>
+
+<style>
+ .diff .add {color:#00ff9d;}
+ .diff .del {color:#ff5f5f;}
+ .diff .header {color:#569cd6;}
+ .diff .hunk {color:#d7ba7d;}
+ .diff .info {color:#848484;}
+</style>
+
+<ol>
+<li>Originalmente, como suele ser en el entorno de Linux, es <i>case sensitive</i>. Generalmente lo prefiero así, es más preciso. Sin embargo, siendo que se trata de un lanzador que quiero que sea rápido, o que me ayude a buscar con facilidad; prefiero que sea <i>case insensitive</i>. En suckless hay un parche oficial para ello (<a href="https://tools.suckless.org/dmenu/patches/case-insensitive/">case-insensitive</a>), que he aplicado:
+<pre><code class="diff"><span class="info">diff --git a/dmenu.c b/dmenu.c</span>
+<span class="hunk">@@ -54,8 +54,9 @@ static Clr *scheme[SchemeLast];</span>
+
+ #include "config.h"
+
+<span class="del">-static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;</span>
+<span class="del">-static char *(*fstrstr)(const char *, const char *) = strstr;</span>
+<span class="add">+static char * cistrstr(const char *s, const char *sub);</span>
+<span class="add">+static int (*fstrncmp)(const char *, const char *, size_t) = strncasecmp;</span>
+<span class="add">+static char *(*fstrstr)(const char *, const char *) = cistrstr;</span>
+
+ static unsigned int
+ textw_clamp(const char *str, unsigned int n)
+<span class="hunk">@@ -733,9 +734,9 @@ main(int argc, char *argv[])</span>
+ topbar = 0;
+ else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
+ fast = 1;
+<span class="del">- else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */</span>
+<span class="del">- fstrncmp = strncasecmp;</span>
+<span class="del">- fstrstr = cistrstr;</span>
+<span class="add">+ else if (!strcmp(argv[i], "-s")) { /* case-sensitive item matching */</span>
+<span class="add">+ fstrncmp = strncmp;</span>
+<span class="add">+ fstrstr = strstr;</span>
+ } else if (i + 1 == argc)
+ usage();
+ /* these options take one argument */</code></pre>
+</li>
+<li>Del mismo modo, y con una pretensión de pérdida de cierta precisión con el objetivo de ganar eficiencia y velocidad de lanzamiento, implemento <b>búsqueda difusa</b> mediante la aplicación del parche <a href="https://tools.suckless.org/dmenu/patches/fuzzymatch/">fuzzymatch</a>, que garantiza que el contenido resultado no tiene por qué coincidir matemáticamente con el patrón de búsqueda (por ejemplo, la búsqueda de <emb>rvt</emb> podrá dar como resultado <emb>urxvt</emb>), en combinación con el parche <a href="https://tools.suckless.org/dmenu/patches/fuzzyhighlight/">fuzzyhighlight</a>, que resalta las coincidencias en la búsqueda.
+<pre><code class="diff"><span class="info">diff --git a/config.h b/config.h</span>
+<span class="info">index 1edb647..64eab2a 100644</span>
+<span class="info">--- a/config.h</span>
+<span class="info">+++ b/config.h</span>
+<span class="hunk">@@ -11,6 +11,8 @@ static const char *colors[SchemeLast][2] = {</span>
+ /* fg bg */
+ [SchemeNorm] = { "#bbbbbb", "#222222" },
+ [SchemeSel] = { "#eeeeee", "#005577" },
+<span class="add">+ [SchemeSelHighlight] = { "#ffc978", "#005577" },</span>
+<span class="add">+ [SchemeNormHighlight] = { "#ffc978", "#222222" },</span>
+ [SchemeOut] = { "#000000", "#00ffff" },
+ };
+ /* -l option; if nonzero, dmenu uses vertical list with given number of lines */
+<span class="info">diff --git a/dmenu.c b/dmenu.c</span>
+<span class="info">index 40f93e0..662131a 100644</span>
+<span class="info">--- a/dmenu.c</span>
+<span class="info">+++ b/dmenu.c</span>
+<span class="hunk">@@ -25,7 +25,9 @@</span>
+ #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
+
+ /* enums */
+<span class="del">-enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */</span>
+<span class="add">+enum { SchemeNorm, SchemeSel, SchemeNormHighlight, SchemeSelHighlight,</span>
+<span class="add">+ SchemeOut, SchemeLast }; /* color schemes */</span>
+<span class="add">+</span>
+
+ struct item {
+ char *text;
+<span class="hunk">@@ -129,9 +131,47 @@ cistrstr(const char *h, const char *n)</span>
+ return NULL;
+ }
+
+<span class="add">+static void</span>
+<span class="add">+drawhighlights(struct item *item, int x, int y, int maxw)</span>
+<span class="add">+{</span>
+<span class="add">+ int i, indent;</span>
+<span class="add">+ char c, *highlight;</span>
+<span class="add">+</span>
+<span class="add">+ if (!(strlen(item->text) && strlen(text)))</span>
+<span class="add">+ return;</span>
+<span class="add">+</span>
+<span class="add">+ drw_setscheme(drw, scheme[item == sel</span>
+<span class="add">+ ? SchemeSelHighlight</span>
+<span class="add">+ : SchemeNormHighlight]);</span>
+<span class="add">+ for (i = 0, highlight = item->text; *highlight && text[i];) {</span>
+<span class="add">+ if (!fstrncmp(highlight, &text[i], 1)) {</span>
+<span class="add">+ /* get indentation */</span>
+<span class="add">+ c = *highlight;</span>
+<span class="add">+ *highlight = '\0';</span>
+<span class="add">+ indent = TEXTW(item->text);</span>
+<span class="add">+ *highlight = c;</span>
+<span class="add">+</span>
+<span class="add">+ /* highlight character */</span>
+<span class="add">+ c = highlight[1];</span>
+<span class="add">+ highlight[1] = '\0';</span>
+<span class="add">+ drw_text(</span>
+<span class="add">+ drw,</span>
+<span class="add">+ x + indent - (lrpad / 2.),</span>
+<span class="add">+ y,</span>
+<span class="add">+ MIN(maxw - indent, TEXTW(highlight) - lrpad),</span>
+<span class="add">+ bh, 0, highlight, 0</span>
+<span class="add">+ );</span>
+<span class="add">+ highlight[1] = c;</span>
+<span class="add">+ ++i;</span>
+<span class="add">+ }</span>
+<span class="add">+ ++highlight;</span>
+<span class="add">+ }</span>
+<span class="add">+}</span>
+<span class="add">+</span>
+ static int
+ drawitem(struct item *item, int x, int y, int w)
+ {
+<span class="add">+ int r;</span>
+ if (item == sel)
+ drw_setscheme(drw, scheme[SchemeSel]);
+ else if (item->out)
+<span class="hunk">@@ -139,7 +179,9 @@ drawitem(struct item *item, int x, int y, int w)</span>
+ else
+ drw_setscheme(drw, scheme[SchemeNorm]);
+
+<span class="del">- return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);</span>
+<span class="add">+ r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);</span>
+<span class="add">+ drawhighlights(item, x, y, w);</span>
+<span class="add">+ return r;</span>
+ }
+
+ static void
+<span class="hunk">@@ -716,7 +758,8 @@ static void</span>
+ usage(void)
+ {
+ die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
+<span class="del">- " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]");</span>
+<span class="add">+ " [-nb color] [-nf color] [-sb color] [-sf color]\n"</span>
+<span class="add">+ " [-nhb color] [-nhf color] [-shb color] [-shf color] [-w windowid]");</span>
+ }
+
+ int
+<span class="hunk">@@ -756,6 +799,14 @@ main(int argc, char *argv[])</span>
+ colors[SchemeSel][ColBg] = argv[++i];
+ else if (!strcmp(argv[i], "-sf")) /* selected foreground color */
+ colors[SchemeSel][ColFg] = argv[++i];
+<span class="add">+ else if (!strcmp(argv[i], "-nhb")) /* normal hi background color */</span>
+<span class="add">+ colors[SchemeNormHighlight][ColBg] = argv[++i];</span>
+<span class="add">+ else if (!strcmp(argv[i], "-nhf")) /* normal hi foreground color */</span>
+<span class="add">+ colors[SchemeNormHighlight][ColFg] = argv[++i];</span>
+<span class="add">+ else if (!strcmp(argv[i], "-shb")) /* selected hi background color */</span>
+<span class="add">+ colors[SchemeSelHighlight][ColBg] = argv[++i];</span>
+<span class="add">+ else if (!strcmp(argv[i], "-shf")) /* selected hi foreground color */</span>
+<span class="add">+ colors[SchemeSelHighlight][ColFg] = argv[++i];</span>
+ else if (!strcmp(argv[i], "-w")) /* embedding window id */
+ embed = argv[++i];
+ else</code></pre>
+<pre><code class="diff"><span class="info">diff --git a/config.h b/config.h</span>
+<span class="info">index 1edb647..a4e6174 100644</span>
+<span class="info">--- a/config.h</span>
+<span class="info">+++ b/config.h</span>
+<span class="hunk">@@ -2,6 +2,7 @@</span>
+ /* Default settings; can be overriden by command line. */
+
+ static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */
+<span class="add">+static int fuzzy = 1; /* -F option; if 0, dmenu doesn't use fuzzy matching */</span>
+ /* -fn option overrides fonts[0]; default X11 font or font set */
+ static const char *fonts[] = {
+ "monospace:size=10"
+<span class="info">diff --git a/config.mk b/config.mk</span>
+<span class="info">index 137f7c8..6a19175 100644</span>
+<span class="info">--- a/config.mk</span>
+<span class="info">+++ b/config.mk</span>
+<span class="hunk">@@ -21,7 +21,7 @@ FREETYPEINC = /usr/include/freetype2</span>
+
+ # includes and libs
+ INCS = -I$(X11INC) -I$(FREETYPEINC)
+<span class="del">-LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS)</span>
+<span class="add">+LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -lm</span>
+
+ # flags
+ CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS)
+<span class="info">diff --git a/dmenu.c b/dmenu.c</span>
+<span class="info">index 40f93e0..03e1f45 100644</span>
+<span class="info">--- a/dmenu.c</span>
+<span class="info">+++ b/dmenu.c</span>
+<span class="hunk">@@ -1,6 +1,7 @@</span>
+ /* See LICENSE file for copyright and license details. */
+ #include <ctype.h>
+ #include <locale.h>
+<span class="add">+#include <math.h></span>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+<span class="hunk">@@ -31,6 +32,7 @@ struct item {</span>
+ char *text;
+ struct item *left, *right;
+ int out;
+<span class="add">+ double distance;</span>
+ };
+
+ static char text[BUFSIZ] = "";
+<span class="hunk">@@ -226,9 +228,93 @@ grabkeyboard(void)</span>
+ die("cannot grab keyboard");
+ }
+
+<span class="add">+int</span>
+<span class="add">+compare_distance(const void *a, const void *b)</span>
+<span class="add">+{</span>
+<span class="add">+ struct item *da = *(struct item **) a;</span>
+<span class="add">+ struct item *db = *(struct item **) b;</span>
+<span class="add">+</span>
+<span class="add">+ if (!db)</span>
+<span class="add">+ return 1;</span>
+<span class="add">+ if (!da)</span>
+<span class="add">+ return -1;</span>
+<span class="add">+</span>
+<span class="add">+ return da->distance == db->distance ? 0 : da->distance < db->distance ? -1 : 1;</span>
+<span class="add">+}</span>
+<span class="add">+</span>
+<span class="add">+void</span>
+<span class="add">+fuzzymatch(void)</span>
+<span class="add">+{</span>
+<span class="add">+ /* bang - we have so much memory */</span>
+<span class="add">+ struct item *it;</span>
+<span class="add">+ struct item **fuzzymatches = NULL;</span>
+<span class="add">+ char c;</span>
+<span class="add">+ int number_of_matches = 0, i, pidx, sidx, eidx;</span>
+<span class="add">+ int text_len = strlen(text), itext_len;</span>
+<span class="add">+</span>
+<span class="add">+ matches = matchend = NULL;</span>
+<span class="add">+</span>
+<span class="add">+ /* walk through all items */</span>
+<span class="add">+ for (it = items; it && it->text; ++it) {</span>
+<span class="add">+ if (text_len) {</span>
+<span class="add">+ itext_len = strlen(it->text);</span>
+<span class="add">+ pidx = 0; /* pointer */</span>
+<span class="add">+ sidx = eidx = -1; /* start of match, end of match */</span>
+<span class="add">+ /* walk through item text */</span>
+<span class="add">+ for (i = 0; i < itext_len && (c = it->text[i]); ++i) {</span>
+<span class="add">+ /* fuzzy match pattern */</span>
+<span class="add">+ if (!fstrncmp(&text[pidx], &c, 1)) {</span>
+<span class="add">+ if(sidx == -1)</span>
+<span class="add">+ sidx = i;</span>
+<span class="add">+ ++pidx;</span>
+<span class="add">+ if (pidx == text_len) {</span>
+<span class="add">+ eidx = i;</span>
+<span class="add">+ break;</span>
+<span class="add">+ }</span>
+<span class="add">+ }</span>
+<span class="add">+ }</span>
+<span class="add">+ /* build list of matches */</span>
+<span class="add">+ if (eidx != -1) {</span>
+<span class="add">+ /* compute distance */</span>
+<span class="add">+ /* add penalty if match starts late (log(sidx+2))</span>
+<span class="add">+ * add penalty for long a match without many matching characters */</span>
+<span class="add">+ it->distance = log(sidx + 2) + (double)(eidx - sidx - text_len);</span>
+<span class="add">+ /* fprintf(stderr, "distance %s %f\n", it->text, it->distance); */</span>
+<span class="add">+ appenditem(it, &matches, &matchend);</span>
+<span class="add">+ ++number_of_matches;</span>
+<span class="add">+ }</span>
+<span class="add">+ } else {</span>
+<span class="add">+ appenditem(it, &matches, &matchend);</span>
+<span class="add">+ }</span>
+<span class="add">+ }</span>
+<span class="add">+</span>
+<span class="add">+ if (number_of_matches) {</span>
+<span class="add">+ /* initialize array with matches */</span>
+<span class="add">+ if (!(fuzzymatches = realloc(fuzzymatches,</span>
+<span class="add">+ number_of_matches * sizeof(struct item *))))</span>
+<span class="add">+ die("cannot realloc %u bytes:", number_of_matches * sizeof(struct item *));</span>
+<span class="add">+ for (i = 0, it = matches; it && i < number_of_matches; ++i, it = it->right)</span>
+<span class="add">+ fuzzymatches[i] = it;</span>
+<span class="add">+ /* sort matches according to distance */</span>
+<span class="add">+ qsort(fuzzymatches, number_of_matches, sizeof(struct item*), compare_distance);</span>
+<span class="add">+ /* rebuild list of matches */</span>
+<span class="add">+ matches = matchend = NULL;</span>
+<span class="add">+ for (i = 0, it = fuzzymatches[i]; i < number_of_matches && it &&</span>
+<span class="add">+ it->text; ++i, it = fuzzymatches[i])</span>
+<span class="add">+ appenditem(it, &matches, &matchend);</span>
+<span class="add">+ free(fuzzymatches);</span>
+<span class="add">+ }</span>
+<span class="add">+ curr = sel = matches;</span>
+<span class="add">+ calcoffsets();</span>
+<span class="add">+}</span>
+<span class="add">+</span>
+ static void
+ match(void)
+ {
+<span class="add">+ if (fuzzy) {</span>
+<span class="add">+ fuzzymatch();</span>
+<span class="add">+ return;</span>
+<span class="add">+ }</span>
+ static char **tokv = NULL;
+ static int tokn = 0;
+
+<span class="hunk">@@ -715,7 +801,7 @@ setup(void)</span>
+ static void
+ usage(void)
+ {
+<span class="del">- die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"</span>
+<span class="add">+ die("usage: dmenu [-bFfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"</span>
+ " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]");
+ }
+
+<span class="hunk">@@ -732,6 +818,8 @@ main(int argc, char *argv[])</span>
+ exit(0);
+ } else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */
+ topbar = 0;
+<span class="add">+ else if (!strcmp(argv[i], "-F")) /* disables fuzzy matching */</span>
+<span class="add">+ fuzzy = 0;</span>
+ else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
+ fast = 1;
+ else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */</code></pre>
+</li>
+</ol>
+</body>
+</html>
diff --git a/index.html b/index.html
@@ -19,6 +19,11 @@
<hr/>
<section class="blog">
+<a href="./dmenu/" class="article">
+ <p class="title">Configuración propia del lanzador de aplicación dmenu</p>
+ <p class="info">25 de junio de 2026 · fjbalon</p><p>dmenu es un lanzador de aplicaciones, extremadamente ligero y rápido para el sistemas de ventanas X, diseñado originalmente para el gestor de ventanas dwm, de suckless. Es una herramienta que, para ...</p>
+</a>
+
<a href="./slock/" class="article">
<p class="title">Configuración propia del bloqueador de pantalla slock</p>
<p class="info">22 de junio de 2026 · fjbalon</p><p>Un software que me gusta bastante de la caja de herramientas de suckless es slock, un bloqueador de pantalla minimalista; tan minimalista que yo creo que es el bloqueador de pantalla x más sencillo q...</p>