sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

commit 0975feac72d28558cf49a47385c10e790e982d73
parent a3fd687904a5a548a6405fe3654efd329d447bc7
Author: Wojciech Madry <madrywojciech99@gmail.com>
Date:   Fri, 17 Jan 2025 18:47:56 +0100

[dmenu][patches][sort_by_popularity]

Added patch for dmenu.
sort_by_popularity sorts items by popularity

Diffstat:
Atools.suckless.org/dmenu/patches/sort_by_popularity/dmenu-sort_by_popularity-20250117-86f0b51.diff | 280+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools.suckless.org/dmenu/patches/sort_by_popularity/index.md | 42++++++++++++++++++++++++++++++++++++++++++
2 files changed, 322 insertions(+), 0 deletions(-)

diff --git a/tools.suckless.org/dmenu/patches/sort_by_popularity/dmenu-sort_by_popularity-20250117-86f0b51.diff b/tools.suckless.org/dmenu/patches/sort_by_popularity/dmenu-sort_by_popularity-20250117-86f0b51.diff @@ -0,0 +1,280 @@ +From e2cbe709046b733d2a770beb315ef5511abe9a19 Mon Sep 17 00:00:00 2001 +From: Wojciech Madry <madrywojciech99@gmail.com> +Date: Fri, 17 Jan 2025 17:11:48 +0100 +Subject: [PATCH] Sort matches by popularity + +Patch will sort all matched instances by popularity. +Each time you open any program, the popularity is increased by 1. +The popularity is stored in the .cache folder. +--- + dmenu.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 165 insertions(+), 21 deletions(-) + +diff --git a/dmenu.c b/dmenu.c +index 804da64..cb26068 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -35,11 +35,13 @@ struct item { + + static char text[BUFSIZ] = ""; + static char *embed; ++static char *popcache = NULL; + static int bh, mw, mh; + static int inputw = 0, promptw; + static int lrpad; /* sum of left and right padding */ + static size_t cursor; + static struct item *items = NULL; ++static struct item *popitems = NULL; + static struct item *matches, *matchend; + static struct item *prev, *curr, *next, *sel; + static int mon = -1, screen; +@@ -105,7 +107,13 @@ cleanup(void) + free(scheme[i]); + for (i = 0; items && items[i].text; ++i) + free(items[i].text); ++ for (i = 0; popitems && popitems[i].text; ++i) ++ free(popitems[i].text); + free(items); ++ free(popitems); ++ if(popcache != NULL) ++ free(popcache); ++ + drw_free(drw); + XSync(dpy, False); + XCloseDisplay(dpy); +@@ -226,6 +234,57 @@ grabkeyboard(void) + die("cannot grab keyboard"); + } + ++static void ++sortitemsbypop(struct item* first, struct item* last) ++{ ++ struct item* item = NULL; ++ struct item* pop = NULL; ++ size_t idx = 0; ++ for (pop = popitems; pop && pop->text; ++pop) { ++ for (item = first; item && item->text && (item <= last || last == NULL); item++) { ++ if(strcmp(item->text, pop->text) == 0) { ++ char* lhs = first[idx].text; ++ first[idx].text = item->text; ++ item->text = lhs; ++ ++idx; ++ break; ++ } ++ } ++ } ++} ++ ++static void ++incpop(struct item* sel) { ++ if(!(sel && sel->text)) ++ return; ++ struct item* pop = NULL; ++ int found = 0; ++ FILE *out; ++ out = fopen(popcache, "w"); ++ if (out == NULL) { ++ printf("Cannot open file '%s'", popcache); ++ return; ++ } ++ char decimal[16] = {'\0'}; ++ for (pop = popitems; pop && pop->text; ++pop) { ++ if(found == 0 && strcmp(pop->text, sel->text) == 0) { ++ pop->out += 1; ++ found = 1; ++ } ++ fputs(pop->text, out); ++ fputs(" ", out); ++ sprintf(decimal, "%i", MIN(pop->out, 999)); ++ fputs(decimal, out); ++ fputs("\n", out); ++ } ++ if(found == 0) { ++ fputs(sel->text, out); ++ fputs(" 1", out); ++ fputs("\n", out); ++ } ++ fclose(out); ++} ++ + static void + match(void) + { +@@ -234,17 +293,16 @@ match(void) + + char buf[sizeof text], *s; + int i, tokc = 0; +- size_t len, textsize; +- struct item *item, *lprefix, *lsubstr, *prefixend, *substrend; ++ size_t textsize; ++ struct item *item, *others, *othersend; + + strcpy(buf, text); + /* separate input text into tokens to be matched individually */ + for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) + if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) + die("cannot realloc %zu bytes:", tokn * sizeof *tokv); +- len = tokc ? strlen(tokv[0]) : 0; + +- matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; ++ matches = others = matchend = othersend = NULL; + textsize = strlen(text) + 1; + for (item = items; item && item->text; item++) { + for (i = 0; i < tokc; i++) +@@ -252,29 +310,20 @@ match(void) + break; + if (i != tokc) /* not all tokens match */ + continue; +- /* exact matches go first, then prefixes, then substrings */ ++ /* exact matches go first, then others */ + if (!tokc || !fstrncmp(text, item->text, textsize)) + appenditem(item, &matches, &matchend); +- else if (!fstrncmp(tokv[0], item->text, len)) +- appenditem(item, &lprefix, &prefixend); + else +- appenditem(item, &lsubstr, &substrend); +- } +- if (lprefix) { +- if (matches) { +- matchend->right = lprefix; +- lprefix->left = matchend; +- } else +- matches = lprefix; +- matchend = prefixend; ++ appenditem(item, &others, &othersend); + } +- if (lsubstr) { ++ if (others) { ++ sortitemsbypop(others, othersend); + if (matches) { +- matchend->right = lsubstr; +- lsubstr->left = matchend; ++ matchend->right = others; ++ others->left = matchend; + } else +- matches = lsubstr; +- matchend = substrend; ++ matches = others; ++ matchend = othersend; + } + curr = sel = matches; + calcoffsets(); +@@ -489,6 +538,7 @@ insert: + break; + case XK_Return: + case XK_KP_Enter: ++ incpop(sel); + puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); + if (!(ev->state & ControlMask)) { + cleanup(); +@@ -711,6 +761,98 @@ setup(void) + drawmenu(); + } + ++static void ++itemize(struct item* item, const char* line, const ssize_t size) ++{ ++ const size_t UNDEF = size + 1; ++ size_t firstchar = UNDEF, lastchar = UNDEF; ++ size_t firstnum = UNDEF; ++ size_t i; ++ int afterspace = 0; ++ for (i = 0 ; i < size ; ++i) { ++ const char c = line[i]; ++ if (c == ' ') { ++ if (firstchar != UNDEF) ++ afterspace = 1; ++ continue; ++ } ++ if (afterspace == 1 && (c >= '0' && c <= '9')) { ++ firstnum = i; ++ break; ++ } ++ if (firstchar == UNDEF) ++ firstchar = i; ++ lastchar = i; ++ } ++ size_t len = lastchar - firstchar + 2; ++ item->text = (char*)malloc(sizeof(char) * len); ++ memcpy(item->text, line + firstchar, len); ++ item->text[len - 1] = '\0'; ++ ++ item->out = 0; ++ if (firstnum != UNDEF) ++ item->out = atoi(line + firstnum); ++} ++ ++static int ++compareitembyoutrev(const void* lhs, const void* rhs) ++{ ++ return ((struct item*)rhs)->out - ((struct item*)lhs)->out; ++} ++ ++static void ++loadpopitems(void) ++{ ++ const char* xdg_cache_home = getenv("XDG_CACHE_HOME"); ++ const char* home = getenv("HOME"); ++ char* cache = NULL; ++ const char* CACHE_FILENAME = "/dmenu_pop.txt"; ++ if(xdg_cache_home != NULL) { ++ size_t xdglen = strlen(xdg_cache_home); ++ cache = (char*)malloc(xdglen + 1); ++ cache[xdglen] = '\0'; ++ strcpy(cache, xdg_cache_home); ++ } else { ++ const char* cachefolder = "/.cache"; ++ size_t hclen = strlen(home) + strlen(cachefolder) + 1; ++ cache = (char*)malloc(hclen + 1); ++ cache[hclen - 1] = '\0'; ++ strcpy(cache, home); ++ strcpy(cache + strlen(home), cachefolder); ++ } ++ const size_t cache_size = strlen(cache) + strlen(CACHE_FILENAME) + 1; ++ popcache = (char*)malloc(sizeof(char) * cache_size); ++ popcache[cache_size - 1] = '\0'; ++ strcpy(popcache, cache); ++ strcpy(popcache + strlen(cache), CACHE_FILENAME); ++ free(cache); ++ ++ FILE * fp; ++ char * line = NULL; ++ size_t i, itemsiz = 0, linesiz = 0; ++ ssize_t len; ++ ++ fp = fopen(popcache, "r"); ++ if (fp == NULL) ++ return; ++ ++ for (i = 0; (len = getline(&line, &linesiz, fp)) != -1; i++) { ++ if (i + 1 >= itemsiz) { ++ itemsiz += 256; ++ if (!(popitems = realloc(popitems, itemsiz * sizeof(*popitems)))) ++ die("cannot realloc %zu bytes:", itemsiz * sizeof(*popitems)); ++ } ++ itemize((struct item*)&popitems[i], line, len); ++ } ++ fclose(fp); ++ if (line) ++ free(line); ++ ++ if (popitems) ++ popitems[i].text = NULL; ++ qsort(popitems, i, sizeof(struct item), compareitembyoutrev); ++} ++ + static void + usage(void) + { +@@ -788,6 +930,8 @@ main(int argc, char *argv[]) + readstdin(); + grabkeyboard(); + } ++ loadpopitems(); ++ sortitemsbypop(items, NULL); + setup(); + run(); + +-- +2.48.1 + diff --git a/tools.suckless.org/dmenu/patches/sort_by_popularity/index.md b/tools.suckless.org/dmenu/patches/sort_by_popularity/index.md @@ -0,0 +1,42 @@ +sort_by_popularity +============= + +The list of programs is sorted by popularity. + +Each time you run the program, its popularity will increase by 1. + +The popularity cache file is stored in: `{CACHE_PATH}/dmenu_pop.txt`. + +Requirements +------------ + +* One of the following system env **shall** be set `XDG_CACHE_HOME` or `HOME` +* Folder `$XDG_CACHE_HOME` or `$HOME/.cache` **shall** exist + +How it works +------------ + +The order of programs is determined by popularity. + +Programs with higher popularity come first. + +The order of programs with the same popularity depends on their position in the cache file. + +Let's assume that we have the following programs: `A`, `B1`, `B2`, `C` + +`[User input] -> [dmenu output] -> [User's choice] -> {PROGRAM: POPULARITY}` + +1. ` ` -> `A`, `B1`, `B2`, `C` -> `C` -> `{}` +2. ` ` -> `C`, `B1`, `B2`, `A` -> `B2` -> `{C: 1}` +3. ` ` -> `C`, `B2`, `B1`, `A` -> ` ` -> `{C: 1, B2: 1}` +4. `B` -> `B2`, `B1` -> `B1` -> `{C: 1, B2: 1}` +5. `B` -> `B2`, `B1` -> `B1` -> `{C: 1, B2: 1, B1: 1}` +6. `B` -> `B1`, `B2` -> ` ` -> `{C: 1, B2: 1, B1: 2}` + +Download +-------- +* [dmenu-sort_by_popularity-20250117-86f0b51.diff](dmenu-sort_by_popularity-20250117-86f0b51.diff) + +Author +------ +* Wojciech Madry - <madrywojciech99@gmail.com>