index.html (18281B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 5 <meta name="viewport" content="width=device-width, initial-scale=1" /> 6 <title>Configuración propia del lanzador de aplicación dmenu</title> 7 <link rel="icon" type="image/png" href="/favicon.png" /> 8 <link rel="stylesheet" type="text/css" href="../article.css" /> 9 </head> 10 <body> 11 <header> 12 <p class="title">Configuración propia del lanzador de aplicación dmenu</p> 13 <p class="date">25 de junio de 2026 • fjbalon</p> 14 </header> 15 <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> 16 <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> 17 <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> 18 <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> 19 20 <ol> 21 <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: 22 <pre><code class="diff"><span class="info">diff --git a/dmenu.c b/dmenu.c</span> 23 <span class="hunk">@@ -54,8 +54,9 @@ static Clr *scheme[SchemeLast];</span> 24 25 #include "config.h" 26 27 <span class="del">-static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;</span> 28 <span class="del">-static char *(*fstrstr)(const char *, const char *) = strstr;</span> 29 <span class="add">+static char * cistrstr(const char *s, const char *sub);</span> 30 <span class="add">+static int (*fstrncmp)(const char *, const char *, size_t) = strncasecmp;</span> 31 <span class="add">+static char *(*fstrstr)(const char *, const char *) = cistrstr;</span> 32 33 static unsigned int 34 textw_clamp(const char *str, unsigned int n) 35 <span class="hunk">@@ -733,9 +734,9 @@ main(int argc, char *argv[])</span> 36 topbar = 0; 37 else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ 38 fast = 1; 39 <span class="del">- else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */</span> 40 <span class="del">- fstrncmp = strncasecmp;</span> 41 <span class="del">- fstrstr = cistrstr;</span> 42 <span class="add">+ else if (!strcmp(argv[i], "-s")) { /* case-sensitive item matching */</span> 43 <span class="add">+ fstrncmp = strncmp;</span> 44 <span class="add">+ fstrstr = strstr;</span> 45 } else if (i + 1 == argc) 46 usage(); 47 /* these options take one argument */</code></pre> 48 </li> 49 <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. 50 <pre><code class="diff"><span class="info">diff --git a/config.h b/config.h</span> 51 <span class="info">index 1edb647..64eab2a 100644</span> 52 <span class="info">--- a/config.h</span> 53 <span class="info">+++ b/config.h</span> 54 <span class="hunk">@@ -11,6 +11,8 @@ static const char *colors[SchemeLast][2] = {</span> 55 /* fg bg */ 56 [SchemeNorm] = { "#bbbbbb", "#222222" }, 57 [SchemeSel] = { "#eeeeee", "#005577" }, 58 <span class="add">+ [SchemeSelHighlight] = { "#ffc978", "#005577" },</span> 59 <span class="add">+ [SchemeNormHighlight] = { "#ffc978", "#222222" },</span> 60 [SchemeOut] = { "#000000", "#00ffff" }, 61 }; 62 /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ 63 <span class="info">diff --git a/dmenu.c b/dmenu.c</span> 64 <span class="info">index 40f93e0..662131a 100644</span> 65 <span class="info">--- a/dmenu.c</span> 66 <span class="info">+++ b/dmenu.c</span> 67 <span class="hunk">@@ -25,7 +25,9 @@</span> 68 #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 69 70 /* enums */ 71 <span class="del">-enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */</span> 72 <span class="add">+enum { SchemeNorm, SchemeSel, SchemeNormHighlight, SchemeSelHighlight,</span> 73 <span class="add">+ SchemeOut, SchemeLast }; /* color schemes */</span> 74 <span class="add">+</span> 75 76 struct item { 77 char *text; 78 <span class="hunk">@@ -129,9 +131,47 @@ cistrstr(const char *h, const char *n)</span> 79 return NULL; 80 } 81 82 <span class="add">+static void</span> 83 <span class="add">+drawhighlights(struct item *item, int x, int y, int maxw)</span> 84 <span class="add">+{</span> 85 <span class="add">+ int i, indent;</span> 86 <span class="add">+ char c, *highlight;</span> 87 <span class="add">+</span> 88 <span class="add">+ if (!(strlen(item->text) && strlen(text)))</span> 89 <span class="add">+ return;</span> 90 <span class="add">+</span> 91 <span class="add">+ drw_setscheme(drw, scheme[item == sel</span> 92 <span class="add">+ ? SchemeSelHighlight</span> 93 <span class="add">+ : SchemeNormHighlight]);</span> 94 <span class="add">+ for (i = 0, highlight = item->text; *highlight && text[i];) {</span> 95 <span class="add">+ if (!fstrncmp(highlight, &text[i], 1)) {</span> 96 <span class="add">+ /* get indentation */</span> 97 <span class="add">+ c = *highlight;</span> 98 <span class="add">+ *highlight = '\0';</span> 99 <span class="add">+ indent = TEXTW(item->text);</span> 100 <span class="add">+ *highlight = c;</span> 101 <span class="add">+</span> 102 <span class="add">+ /* highlight character */</span> 103 <span class="add">+ c = highlight[1];</span> 104 <span class="add">+ highlight[1] = '\0';</span> 105 <span class="add">+ drw_text(</span> 106 <span class="add">+ drw,</span> 107 <span class="add">+ x + indent - (lrpad / 2.),</span> 108 <span class="add">+ y,</span> 109 <span class="add">+ MIN(maxw - indent, TEXTW(highlight) - lrpad),</span> 110 <span class="add">+ bh, 0, highlight, 0</span> 111 <span class="add">+ );</span> 112 <span class="add">+ highlight[1] = c;</span> 113 <span class="add">+ ++i;</span> 114 <span class="add">+ }</span> 115 <span class="add">+ ++highlight;</span> 116 <span class="add">+ }</span> 117 <span class="add">+}</span> 118 <span class="add">+</span> 119 static int 120 drawitem(struct item *item, int x, int y, int w) 121 { 122 <span class="add">+ int r;</span> 123 if (item == sel) 124 drw_setscheme(drw, scheme[SchemeSel]); 125 else if (item->out) 126 <span class="hunk">@@ -139,7 +179,9 @@ drawitem(struct item *item, int x, int y, int w)</span> 127 else 128 drw_setscheme(drw, scheme[SchemeNorm]); 129 130 <span class="del">- return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);</span> 131 <span class="add">+ r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);</span> 132 <span class="add">+ drawhighlights(item, x, y, w);</span> 133 <span class="add">+ return r;</span> 134 } 135 136 static void 137 <span class="hunk">@@ -716,7 +758,8 @@ static void</span> 138 usage(void) 139 { 140 die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" 141 <span class="del">- " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]");</span> 142 <span class="add">+ " [-nb color] [-nf color] [-sb color] [-sf color]\n"</span> 143 <span class="add">+ " [-nhb color] [-nhf color] [-shb color] [-shf color] [-w windowid]");</span> 144 } 145 146 int 147 <span class="hunk">@@ -756,6 +799,14 @@ main(int argc, char *argv[])</span> 148 colors[SchemeSel][ColBg] = argv[++i]; 149 else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ 150 colors[SchemeSel][ColFg] = argv[++i]; 151 <span class="add">+ else if (!strcmp(argv[i], "-nhb")) /* normal hi background color */</span> 152 <span class="add">+ colors[SchemeNormHighlight][ColBg] = argv[++i];</span> 153 <span class="add">+ else if (!strcmp(argv[i], "-nhf")) /* normal hi foreground color */</span> 154 <span class="add">+ colors[SchemeNormHighlight][ColFg] = argv[++i];</span> 155 <span class="add">+ else if (!strcmp(argv[i], "-shb")) /* selected hi background color */</span> 156 <span class="add">+ colors[SchemeSelHighlight][ColBg] = argv[++i];</span> 157 <span class="add">+ else if (!strcmp(argv[i], "-shf")) /* selected hi foreground color */</span> 158 <span class="add">+ colors[SchemeSelHighlight][ColFg] = argv[++i];</span> 159 else if (!strcmp(argv[i], "-w")) /* embedding window id */ 160 embed = argv[++i]; 161 else</code></pre> 162 <pre><code class="diff"><span class="info">diff --git a/config.h b/config.h</span> 163 <span class="info">index 1edb647..a4e6174 100644</span> 164 <span class="info">--- a/config.h</span> 165 <span class="info">+++ b/config.h</span> 166 <span class="hunk">@@ -2,6 +2,7 @@</span> 167 /* Default settings; can be overriden by command line. */ 168 169 static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ 170 <span class="add">+static int fuzzy = 1; /* -F option; if 0, dmenu doesn't use fuzzy matching */</span> 171 /* -fn option overrides fonts[0]; default X11 font or font set */ 172 static const char *fonts[] = { 173 "monospace:size=10" 174 <span class="info">diff --git a/config.mk b/config.mk</span> 175 <span class="info">index 137f7c8..6a19175 100644</span> 176 <span class="info">--- a/config.mk</span> 177 <span class="info">+++ b/config.mk</span> 178 <span class="hunk">@@ -21,7 +21,7 @@ FREETYPEINC = /usr/include/freetype2</span> 179 180 # includes and libs 181 INCS = -I$(X11INC) -I$(FREETYPEINC) 182 <span class="del">-LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS)</span> 183 <span class="add">+LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -lm</span> 184 185 # flags 186 CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) 187 <span class="info">diff --git a/dmenu.c b/dmenu.c</span> 188 <span class="info">index 40f93e0..03e1f45 100644</span> 189 <span class="info">--- a/dmenu.c</span> 190 <span class="info">+++ b/dmenu.c</span> 191 <span class="hunk">@@ -1,6 +1,7 @@</span> 192 /* See LICENSE file for copyright and license details. */ 193 #include <ctype.h> 194 #include <locale.h> 195 <span class="add">+#include <math.h></span> 196 #include <stdio.h> 197 #include <stdlib.h> 198 #include <string.h> 199 <span class="hunk">@@ -31,6 +32,7 @@ struct item {</span> 200 char *text; 201 struct item *left, *right; 202 int out; 203 <span class="add">+ double distance;</span> 204 }; 205 206 static char text[BUFSIZ] = ""; 207 <span class="hunk">@@ -226,9 +228,93 @@ grabkeyboard(void)</span> 208 die("cannot grab keyboard"); 209 } 210 211 <span class="add">+int</span> 212 <span class="add">+compare_distance(const void *a, const void *b)</span> 213 <span class="add">+{</span> 214 <span class="add">+ struct item *da = *(struct item **) a;</span> 215 <span class="add">+ struct item *db = *(struct item **) b;</span> 216 <span class="add">+</span> 217 <span class="add">+ if (!db)</span> 218 <span class="add">+ return 1;</span> 219 <span class="add">+ if (!da)</span> 220 <span class="add">+ return -1;</span> 221 <span class="add">+</span> 222 <span class="add">+ return da->distance == db->distance ? 0 : da->distance < db->distance ? -1 : 1;</span> 223 <span class="add">+}</span> 224 <span class="add">+</span> 225 <span class="add">+void</span> 226 <span class="add">+fuzzymatch(void)</span> 227 <span class="add">+{</span> 228 <span class="add">+ /* bang - we have so much memory */</span> 229 <span class="add">+ struct item *it;</span> 230 <span class="add">+ struct item **fuzzymatches = NULL;</span> 231 <span class="add">+ char c;</span> 232 <span class="add">+ int number_of_matches = 0, i, pidx, sidx, eidx;</span> 233 <span class="add">+ int text_len = strlen(text), itext_len;</span> 234 <span class="add">+</span> 235 <span class="add">+ matches = matchend = NULL;</span> 236 <span class="add">+</span> 237 <span class="add">+ /* walk through all items */</span> 238 <span class="add">+ for (it = items; it && it->text; ++it) {</span> 239 <span class="add">+ if (text_len) {</span> 240 <span class="add">+ itext_len = strlen(it->text);</span> 241 <span class="add">+ pidx = 0; /* pointer */</span> 242 <span class="add">+ sidx = eidx = -1; /* start of match, end of match */</span> 243 <span class="add">+ /* walk through item text */</span> 244 <span class="add">+ for (i = 0; i < itext_len && (c = it->text[i]); ++i) {</span> 245 <span class="add">+ /* fuzzy match pattern */</span> 246 <span class="add">+ if (!fstrncmp(&text[pidx], &c, 1)) {</span> 247 <span class="add">+ if(sidx == -1)</span> 248 <span class="add">+ sidx = i;</span> 249 <span class="add">+ ++pidx;</span> 250 <span class="add">+ if (pidx == text_len) {</span> 251 <span class="add">+ eidx = i;</span> 252 <span class="add">+ break;</span> 253 <span class="add">+ }</span> 254 <span class="add">+ }</span> 255 <span class="add">+ }</span> 256 <span class="add">+ /* build list of matches */</span> 257 <span class="add">+ if (eidx != -1) {</span> 258 <span class="add">+ /* compute distance */</span> 259 <span class="add">+ /* add penalty if match starts late (log(sidx+2))</span> 260 <span class="add">+ * add penalty for long a match without many matching characters */</span> 261 <span class="add">+ it->distance = log(sidx + 2) + (double)(eidx - sidx - text_len);</span> 262 <span class="add">+ /* fprintf(stderr, "distance %s %f\n", it->text, it->distance); */</span> 263 <span class="add">+ appenditem(it, &matches, &matchend);</span> 264 <span class="add">+ ++number_of_matches;</span> 265 <span class="add">+ }</span> 266 <span class="add">+ } else {</span> 267 <span class="add">+ appenditem(it, &matches, &matchend);</span> 268 <span class="add">+ }</span> 269 <span class="add">+ }</span> 270 <span class="add">+</span> 271 <span class="add">+ if (number_of_matches) {</span> 272 <span class="add">+ /* initialize array with matches */</span> 273 <span class="add">+ if (!(fuzzymatches = realloc(fuzzymatches,</span> 274 <span class="add">+ number_of_matches * sizeof(struct item *))))</span> 275 <span class="add">+ die("cannot realloc %u bytes:", number_of_matches * sizeof(struct item *));</span> 276 <span class="add">+ for (i = 0, it = matches; it && i < number_of_matches; ++i, it = it->right)</span> 277 <span class="add">+ fuzzymatches[i] = it;</span> 278 <span class="add">+ /* sort matches according to distance */</span> 279 <span class="add">+ qsort(fuzzymatches, number_of_matches, sizeof(struct item*), compare_distance);</span> 280 <span class="add">+ /* rebuild list of matches */</span> 281 <span class="add">+ matches = matchend = NULL;</span> 282 <span class="add">+ for (i = 0, it = fuzzymatches[i]; i < number_of_matches && it &&</span> 283 <span class="add">+ it->text; ++i, it = fuzzymatches[i])</span> 284 <span class="add">+ appenditem(it, &matches, &matchend);</span> 285 <span class="add">+ free(fuzzymatches);</span> 286 <span class="add">+ }</span> 287 <span class="add">+ curr = sel = matches;</span> 288 <span class="add">+ calcoffsets();</span> 289 <span class="add">+}</span> 290 <span class="add">+</span> 291 static void 292 match(void) 293 { 294 <span class="add">+ if (fuzzy) {</span> 295 <span class="add">+ fuzzymatch();</span> 296 <span class="add">+ return;</span> 297 <span class="add">+ }</span> 298 static char **tokv = NULL; 299 static int tokn = 0; 300 301 <span class="hunk">@@ -715,7 +801,7 @@ setup(void)</span> 302 static void 303 usage(void) 304 { 305 <span class="del">- die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"</span> 306 <span class="add">+ die("usage: dmenu [-bFfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"</span> 307 " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]"); 308 } 309 310 <span class="hunk">@@ -732,6 +818,8 @@ main(int argc, char *argv[])</span> 311 exit(0); 312 } else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */ 313 topbar = 0; 314 <span class="add">+ else if (!strcmp(argv[i], "-F")) /* disables fuzzy matching */</span> 315 <span class="add">+ fuzzy = 0;</span> 316 else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ 317 fast = 1; 318 else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */</code></pre> 319 </li> 320 </ol> 321 </body> 322 </html>