sites

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

dmenu-sort_by_popularity-20250117-86f0b51.diff (7298B)


      1 From e2cbe709046b733d2a770beb315ef5511abe9a19 Mon Sep 17 00:00:00 2001
      2 From: Wojciech Madry <madrywojciech99@gmail.com>
      3 Date: Fri, 17 Jan 2025 17:11:48 +0100
      4 Subject: [PATCH] Sort matches by popularity
      5 
      6 Patch will sort all matched instances by popularity.
      7 Each time you open any program, the popularity is increased by 1.
      8 The popularity is stored in the .cache folder.
      9 ---
     10  dmenu.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++++-------
     11  1 file changed, 165 insertions(+), 21 deletions(-)
     12 
     13 diff --git a/dmenu.c b/dmenu.c
     14 index 804da64..cb26068 100644
     15 --- a/dmenu.c
     16 +++ b/dmenu.c
     17 @@ -35,11 +35,13 @@ struct item {
     18  
     19  static char text[BUFSIZ] = "";
     20  static char *embed;
     21 +static char *popcache = NULL;
     22  static int bh, mw, mh;
     23  static int inputw = 0, promptw;
     24  static int lrpad; /* sum of left and right padding */
     25  static size_t cursor;
     26  static struct item *items = NULL;
     27 +static struct item *popitems = NULL;
     28  static struct item *matches, *matchend;
     29  static struct item *prev, *curr, *next, *sel;
     30  static int mon = -1, screen;
     31 @@ -105,7 +107,13 @@ cleanup(void)
     32  		free(scheme[i]);
     33  	for (i = 0; items && items[i].text; ++i)
     34  		free(items[i].text);
     35 +	for (i = 0; popitems && popitems[i].text; ++i)
     36 +		free(popitems[i].text);
     37  	free(items);
     38 +	free(popitems);
     39 +	if(popcache != NULL)
     40 +		free(popcache);
     41 +
     42  	drw_free(drw);
     43  	XSync(dpy, False);
     44  	XCloseDisplay(dpy);
     45 @@ -226,6 +234,57 @@ grabkeyboard(void)
     46  	die("cannot grab keyboard");
     47  }
     48  
     49 +static void
     50 +sortitemsbypop(struct item* first, struct item* last)
     51 +{
     52 +	struct item* item = NULL;
     53 +	struct item* pop = NULL;
     54 +	size_t idx = 0;
     55 +	for (pop = popitems; pop && pop->text; ++pop) {
     56 +		for (item = first; item && item->text && (item <= last || last == NULL); item++) {
     57 +			if(strcmp(item->text, pop->text) == 0) {
     58 +				char* lhs = first[idx].text;
     59 +				first[idx].text = item->text;
     60 +				item->text = lhs;
     61 +				++idx;
     62 +				break;
     63 +			}
     64 +		}
     65 +	}
     66 +}
     67 +
     68 +static void
     69 +incpop(struct item* sel) {
     70 +	if(!(sel && sel->text))
     71 +		return;
     72 +	struct item* pop = NULL;
     73 +	int found = 0;
     74 +	FILE *out;
     75 +	out = fopen(popcache, "w");
     76 +	if (out == NULL) {
     77 +		printf("Cannot open file '%s'", popcache);
     78 +		return;
     79 +	}
     80 +	char decimal[16] = {'\0'};
     81 +	for (pop = popitems; pop && pop->text; ++pop) {
     82 +		if(found == 0 && strcmp(pop->text, sel->text) == 0) {
     83 +			pop->out += 1;
     84 +			found = 1;
     85 +		}
     86 +		fputs(pop->text, out);
     87 +		fputs(" ", out);
     88 +		sprintf(decimal, "%i", MIN(pop->out, 999));
     89 +		fputs(decimal, out);
     90 +		fputs("\n", out);
     91 +	}
     92 +	if(found == 0) {
     93 +		fputs(sel->text, out);
     94 +		fputs(" 1", out);
     95 +		fputs("\n", out);
     96 +	}
     97 +	fclose(out);
     98 +}
     99 +
    100  static void
    101  match(void)
    102  {
    103 @@ -234,17 +293,16 @@ match(void)
    104  
    105  	char buf[sizeof text], *s;
    106  	int i, tokc = 0;
    107 -	size_t len, textsize;
    108 -	struct item *item, *lprefix, *lsubstr, *prefixend, *substrend;
    109 +	size_t textsize;
    110 +	struct item *item, *others, *othersend;
    111  
    112  	strcpy(buf, text);
    113  	/* separate input text into tokens to be matched individually */
    114  	for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " "))
    115  		if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv)))
    116  			die("cannot realloc %zu bytes:", tokn * sizeof *tokv);
    117 -	len = tokc ? strlen(tokv[0]) : 0;
    118  
    119 -	matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL;
    120 +	matches = others = matchend = othersend = NULL;
    121  	textsize = strlen(text) + 1;
    122  	for (item = items; item && item->text; item++) {
    123  		for (i = 0; i < tokc; i++)
    124 @@ -252,29 +310,20 @@ match(void)
    125  				break;
    126  		if (i != tokc) /* not all tokens match */
    127  			continue;
    128 -		/* exact matches go first, then prefixes, then substrings */
    129 +		/* exact matches go first, then others */
    130  		if (!tokc || !fstrncmp(text, item->text, textsize))
    131  			appenditem(item, &matches, &matchend);
    132 -		else if (!fstrncmp(tokv[0], item->text, len))
    133 -			appenditem(item, &lprefix, &prefixend);
    134  		else
    135 -			appenditem(item, &lsubstr, &substrend);
    136 -	}
    137 -	if (lprefix) {
    138 -		if (matches) {
    139 -			matchend->right = lprefix;
    140 -			lprefix->left = matchend;
    141 -		} else
    142 -			matches = lprefix;
    143 -		matchend = prefixend;
    144 +			appenditem(item, &others, &othersend);
    145  	}
    146 -	if (lsubstr) {
    147 +	if (others) {
    148 +		sortitemsbypop(others, othersend);
    149  		if (matches) {
    150 -			matchend->right = lsubstr;
    151 -			lsubstr->left = matchend;
    152 +			matchend->right = others;
    153 +			others->left = matchend;
    154  		} else
    155 -			matches = lsubstr;
    156 -		matchend = substrend;
    157 +			matches = others;
    158 +		matchend = othersend;
    159  	}
    160  	curr = sel = matches;
    161  	calcoffsets();
    162 @@ -489,6 +538,7 @@ insert:
    163  		break;
    164  	case XK_Return:
    165  	case XK_KP_Enter:
    166 +		incpop(sel);
    167  		puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
    168  		if (!(ev->state & ControlMask)) {
    169  			cleanup();
    170 @@ -711,6 +761,98 @@ setup(void)
    171  	drawmenu();
    172  }
    173  
    174 +static void
    175 +itemize(struct item* item, const char* line, const ssize_t size)
    176 +{
    177 +	const size_t UNDEF = size + 1;
    178 +	size_t firstchar = UNDEF, lastchar = UNDEF;
    179 +	size_t firstnum = UNDEF;
    180 +	size_t i;
    181 +	int afterspace = 0;
    182 +	for (i = 0 ; i < size ; ++i) {
    183 +		const char c = line[i];
    184 +		if (c == ' ') {
    185 +			if (firstchar != UNDEF)
    186 +				afterspace = 1;
    187 +			continue;
    188 +		}
    189 +		if (afterspace == 1 && (c >= '0' && c <= '9')) {
    190 +			firstnum = i;
    191 +			break;
    192 +		}
    193 +		if (firstchar == UNDEF)
    194 +			firstchar = i;
    195 +		lastchar = i;
    196 +	}
    197 +	size_t len = lastchar - firstchar + 2;
    198 +	item->text = (char*)malloc(sizeof(char) * len);
    199 +	memcpy(item->text, line + firstchar, len);
    200 +	item->text[len - 1] = '\0';
    201 +
    202 +	item->out = 0;
    203 +	if (firstnum != UNDEF)
    204 +		item->out = atoi(line + firstnum);
    205 +}
    206 +
    207 +static int
    208 +compareitembyoutrev(const void* lhs, const void* rhs)
    209 +{
    210 +	return ((struct item*)rhs)->out - ((struct item*)lhs)->out;
    211 +}
    212 +
    213 +static void
    214 +loadpopitems(void)
    215 +{
    216 +	const char* xdg_cache_home = getenv("XDG_CACHE_HOME");
    217 +	const char* home = getenv("HOME");
    218 +	char* cache = NULL;
    219 +	const char* CACHE_FILENAME = "/dmenu_pop.txt";
    220 +	if(xdg_cache_home != NULL) {
    221 +		size_t xdglen = strlen(xdg_cache_home);
    222 +		cache = (char*)malloc(xdglen + 1);
    223 +		cache[xdglen] = '\0';
    224 +		strcpy(cache, xdg_cache_home);
    225 +	} else {
    226 +		const char* cachefolder = "/.cache";
    227 +		size_t hclen = strlen(home) + strlen(cachefolder) + 1;
    228 +		cache = (char*)malloc(hclen + 1);
    229 +		cache[hclen - 1] = '\0';
    230 +		strcpy(cache, home);
    231 +		strcpy(cache + strlen(home), cachefolder);
    232 +	}
    233 +	const size_t cache_size = strlen(cache) + strlen(CACHE_FILENAME) + 1;
    234 +	popcache = (char*)malloc(sizeof(char) * cache_size);
    235 +	popcache[cache_size - 1] = '\0';
    236 +	strcpy(popcache, cache);
    237 +	strcpy(popcache + strlen(cache), CACHE_FILENAME);
    238 +	free(cache);
    239 +
    240 +	FILE * fp;
    241 +	char * line = NULL;
    242 +	size_t i, itemsiz = 0, linesiz = 0;
    243 +	ssize_t len;
    244 +
    245 +	fp = fopen(popcache, "r");
    246 +	if (fp == NULL)
    247 +		return;
    248 +
    249 +	for (i = 0; (len = getline(&line, &linesiz, fp)) != -1; i++) {
    250 +		if (i + 1 >= itemsiz) {
    251 +			itemsiz += 256;
    252 +			if (!(popitems = realloc(popitems, itemsiz * sizeof(*popitems))))
    253 +				die("cannot realloc %zu bytes:", itemsiz * sizeof(*popitems));
    254 +		}
    255 +		itemize((struct item*)&popitems[i], line, len);
    256 +	}
    257 +	fclose(fp);
    258 +	if (line)
    259 +		free(line);
    260 +
    261 +	if (popitems)
    262 +		popitems[i].text = NULL;
    263 +	qsort(popitems, i, sizeof(struct item), compareitembyoutrev);
    264 +}
    265 +
    266  static void
    267  usage(void)
    268  {
    269 @@ -788,6 +930,8 @@ main(int argc, char *argv[])
    270  		readstdin();
    271  		grabkeyboard();
    272  	}
    273 +	loadpopitems();
    274 +	sortitemsbypop(items, NULL);
    275  	setup();
    276  	run();
    277  
    278 -- 
    279 2.48.1
    280