Configuración propia del lanzador de aplicación dmenu
25 de junio de 2026 • fjbalon
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.
Puede encontrar mi propia versión del código en mi servidor de git: /git/dmenu/.
La versión inicial es muy sencilla, al ser llamado mediante el comando
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:
diff --git a/dmenu.c b/dmenu.c
@@ -54,8 +54,9 @@ static Clr *scheme[SchemeLast];
#include "config.h"
-static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
-static char *(*fstrstr)(const char *, const char *) = strstr;
+static char * cistrstr(const char *s, const char *sub);
+static int (*fstrncmp)(const char *, const char *, size_t) = strncasecmp;
+static char *(*fstrstr)(const char *, const char *) = cistrstr;
static unsigned int
textw_clamp(const char *str, unsigned int n)
@@ -733,9 +734,9 @@ main(int argc, char *argv[])
topbar = 0;
else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
fast = 1;
- else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
- fstrncmp = strncasecmp;
- fstrstr = cistrstr;
+ else if (!strcmp(argv[i], "-s")) { /* case-sensitive item matching */
+ fstrncmp = strncmp;
+ fstrstr = strstr;
} else if (i + 1 == argc)
usage();
/* these options take one argument */
diff --git a/config.h b/config.h
index 1edb647..64eab2a 100644
--- a/config.h
+++ b/config.h
@@ -11,6 +11,8 @@ static const char *colors[SchemeLast][2] = {
/* fg bg */
[SchemeNorm] = { "#bbbbbb", "#222222" },
[SchemeSel] = { "#eeeeee", "#005577" },
+ [SchemeSelHighlight] = { "#ffc978", "#005577" },
+ [SchemeNormHighlight] = { "#ffc978", "#222222" },
[SchemeOut] = { "#000000", "#00ffff" },
};
/* -l option; if nonzero, dmenu uses vertical list with given number of lines */
diff --git a/dmenu.c b/dmenu.c
index 40f93e0..662131a 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -25,7 +25,9 @@
#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
/* enums */
-enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
+enum { SchemeNorm, SchemeSel, SchemeNormHighlight, SchemeSelHighlight,
+ SchemeOut, SchemeLast }; /* color schemes */
+
struct item {
char *text;
@@ -129,9 +131,47 @@ cistrstr(const char *h, const char *n)
return NULL;
}
+static void
+drawhighlights(struct item *item, int x, int y, int maxw)
+{
+ int i, indent;
+ char c, *highlight;
+
+ if (!(strlen(item->text) && strlen(text)))
+ return;
+
+ drw_setscheme(drw, scheme[item == sel
+ ? SchemeSelHighlight
+ : SchemeNormHighlight]);
+ for (i = 0, highlight = item->text; *highlight && text[i];) {
+ if (!fstrncmp(highlight, &text[i], 1)) {
+ /* get indentation */
+ c = *highlight;
+ *highlight = '\0';
+ indent = TEXTW(item->text);
+ *highlight = c;
+
+ /* highlight character */
+ c = highlight[1];
+ highlight[1] = '\0';
+ drw_text(
+ drw,
+ x + indent - (lrpad / 2.),
+ y,
+ MIN(maxw - indent, TEXTW(highlight) - lrpad),
+ bh, 0, highlight, 0
+ );
+ highlight[1] = c;
+ ++i;
+ }
+ ++highlight;
+ }
+}
+
static int
drawitem(struct item *item, int x, int y, int w)
{
+ int r;
if (item == sel)
drw_setscheme(drw, scheme[SchemeSel]);
else if (item->out)
@@ -139,7 +179,9 @@ drawitem(struct item *item, int x, int y, int w)
else
drw_setscheme(drw, scheme[SchemeNorm]);
- return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);
+ r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);
+ drawhighlights(item, x, y, w);
+ return r;
}
static void
@@ -716,7 +758,8 @@ static void
usage(void)
{
die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
- " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]");
+ " [-nb color] [-nf color] [-sb color] [-sf color]\n"
+ " [-nhb color] [-nhf color] [-shb color] [-shf color] [-w windowid]");
}
int
@@ -756,6 +799,14 @@ main(int argc, char *argv[])
colors[SchemeSel][ColBg] = argv[++i];
else if (!strcmp(argv[i], "-sf")) /* selected foreground color */
colors[SchemeSel][ColFg] = argv[++i];
+ else if (!strcmp(argv[i], "-nhb")) /* normal hi background color */
+ colors[SchemeNormHighlight][ColBg] = argv[++i];
+ else if (!strcmp(argv[i], "-nhf")) /* normal hi foreground color */
+ colors[SchemeNormHighlight][ColFg] = argv[++i];
+ else if (!strcmp(argv[i], "-shb")) /* selected hi background color */
+ colors[SchemeSelHighlight][ColBg] = argv[++i];
+ else if (!strcmp(argv[i], "-shf")) /* selected hi foreground color */
+ colors[SchemeSelHighlight][ColFg] = argv[++i];
else if (!strcmp(argv[i], "-w")) /* embedding window id */
embed = argv[++i];
else
diff --git a/config.h b/config.h
index 1edb647..a4e6174 100644
--- a/config.h
+++ b/config.h
@@ -2,6 +2,7 @@
/* Default settings; can be overriden by command line. */
static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */
+static int fuzzy = 1; /* -F option; if 0, dmenu doesn't use fuzzy matching */
/* -fn option overrides fonts[0]; default X11 font or font set */
static const char *fonts[] = {
"monospace:size=10"
diff --git a/config.mk b/config.mk
index 137f7c8..6a19175 100644
--- a/config.mk
+++ b/config.mk
@@ -21,7 +21,7 @@ FREETYPEINC = /usr/include/freetype2
# includes and libs
INCS = -I$(X11INC) -I$(FREETYPEINC)
-LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS)
+LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -lm
# flags
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS)
diff --git a/dmenu.c b/dmenu.c
index 40f93e0..03e1f45 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -1,6 +1,7 @@
/* See LICENSE file for copyright and license details. */
#include <ctype.h>
#include <locale.h>
+#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -31,6 +32,7 @@ struct item {
char *text;
struct item *left, *right;
int out;
+ double distance;
};
static char text[BUFSIZ] = "";
@@ -226,9 +228,93 @@ grabkeyboard(void)
die("cannot grab keyboard");
}
+int
+compare_distance(const void *a, const void *b)
+{
+ struct item *da = *(struct item **) a;
+ struct item *db = *(struct item **) b;
+
+ if (!db)
+ return 1;
+ if (!da)
+ return -1;
+
+ return da->distance == db->distance ? 0 : da->distance < db->distance ? -1 : 1;
+}
+
+void
+fuzzymatch(void)
+{
+ /* bang - we have so much memory */
+ struct item *it;
+ struct item **fuzzymatches = NULL;
+ char c;
+ int number_of_matches = 0, i, pidx, sidx, eidx;
+ int text_len = strlen(text), itext_len;
+
+ matches = matchend = NULL;
+
+ /* walk through all items */
+ for (it = items; it && it->text; ++it) {
+ if (text_len) {
+ itext_len = strlen(it->text);
+ pidx = 0; /* pointer */
+ sidx = eidx = -1; /* start of match, end of match */
+ /* walk through item text */
+ for (i = 0; i < itext_len && (c = it->text[i]); ++i) {
+ /* fuzzy match pattern */
+ if (!fstrncmp(&text[pidx], &c, 1)) {
+ if(sidx == -1)
+ sidx = i;
+ ++pidx;
+ if (pidx == text_len) {
+ eidx = i;
+ break;
+ }
+ }
+ }
+ /* build list of matches */
+ if (eidx != -1) {
+ /* compute distance */
+ /* add penalty if match starts late (log(sidx+2))
+ * add penalty for long a match without many matching characters */
+ it->distance = log(sidx + 2) + (double)(eidx - sidx - text_len);
+ /* fprintf(stderr, "distance %s %f\n", it->text, it->distance); */
+ appenditem(it, &matches, &matchend);
+ ++number_of_matches;
+ }
+ } else {
+ appenditem(it, &matches, &matchend);
+ }
+ }
+
+ if (number_of_matches) {
+ /* initialize array with matches */
+ if (!(fuzzymatches = realloc(fuzzymatches,
+ number_of_matches * sizeof(struct item *))))
+ die("cannot realloc %u bytes:", number_of_matches * sizeof(struct item *));
+ for (i = 0, it = matches; it && i < number_of_matches; ++i, it = it->right)
+ fuzzymatches[i] = it;
+ /* sort matches according to distance */
+ qsort(fuzzymatches, number_of_matches, sizeof(struct item*), compare_distance);
+ /* rebuild list of matches */
+ matches = matchend = NULL;
+ for (i = 0, it = fuzzymatches[i]; i < number_of_matches && it &&
+ it->text; ++i, it = fuzzymatches[i])
+ appenditem(it, &matches, &matchend);
+ free(fuzzymatches);
+ }
+ curr = sel = matches;
+ calcoffsets();
+}
+
static void
match(void)
{
+ if (fuzzy) {
+ fuzzymatch();
+ return;
+ }
static char **tokv = NULL;
static int tokn = 0;
@@ -715,7 +801,7 @@ setup(void)
static void
usage(void)
{
- die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
+ die("usage: dmenu [-bFfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
" [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]");
}
@@ -732,6 +818,8 @@ main(int argc, char *argv[])
exit(0);
} else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */
topbar = 0;
+ else if (!strcmp(argv[i], "-F")) /* disables fuzzy matching */
+ fuzzy = 0;
else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
fast = 1;
else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */