diff --git a/docs/docs.md b/docs/docs.md index 2c457df..a2c6a04 100644 --- a/docs/docs.md +++ b/docs/docs.md @@ -80,6 +80,8 @@ See a list below for a list. - spmenu -shl - Show highlight - spmenu -si - Show image - spmenu -xrdb - Load .Xresources on runtime +- spmenu -nxrdb - Don't load .Xresources on runtime +- spmenu -m monitor - Specify a monitor to run spmenu on - spmenu -w window id - Embed spmenu inside window id - spmenu -H hist file - Specify a path to save the history to - spmenu -ig gaps - Set image gaps to gaps diff --git a/libs/argv.c b/libs/argv.c index 3b28236..c9e1a72 100644 --- a/libs/argv.c +++ b/libs/argv.c @@ -123,11 +123,11 @@ readargs(int argc, char *argv[]) indentitems = 1; } else if (!strcmp(argv[i], "-nip")) { /* don't indent to prompt width */ indentitems = 0; - } else if (i + 1 == argc) - fprintf(stderr, "spmenu: Invalid argument: '%s'\n", argv[i]); + } else if (i + 1 == argc) { + fprintf(stderr, "spmenu: The '%s' option requires an argument.\n", argv[i]); /* these options take one argument */ - else if (!strcmp(argv[i], "-g")) { /* number of columns in grid */ + } else if (!strcmp(argv[i], "-g")) { /* number of columns in grid */ columns = atoi(argv[++i]); if (lines == 0) lines = 1; } else if (!strcmp(argv[i], "-Ps")) { /* password symbol */ @@ -164,7 +164,6 @@ readargs(int argc, char *argv[]) else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ prompt = argv[++i]; else if (!strcmp(argv[i], "-fn")) /* font or font set */ - //strcpy(font[0], argv[++i]); font[0] = argv[++i]; else if (!strcmp(argv[i], "-nmt")) /* normal mode text */ strcpy(normtext, argv[++i]); diff --git a/libs/img-c.c b/libs/img-c.c new file mode 100644 index 0000000..d3465c6 --- /dev/null +++ b/libs/img-c.c @@ -0,0 +1,353 @@ +void +setimagesize(int width, int height) +{ + #if !USEIMAGE + return; + #endif + + int oih = 0; + int oiw = 0; + + /* this makes sure we cannot scale the image up or down too much */ + if ((!image && height < imageheight) || (!image && width < imagewidth) || width > mw || hideimage) return; + + oih = imageheight; + oiw = imagewidth; + + imageheight = height; + imagewidth = width; + + drawimage(); + + needredraw = 0; + + if (!image) { + imageheight = oih; + imagewidth = oiw; + return; + } + + drawmenu(); +} + +void +flipimage(void) +{ + switch (flip) { + case 1: /* horizontal */ + imlib_image_flip_horizontal(); + break; + case 2: /* vertical */ + imlib_image_flip_vertical(); + break; + case 3: /* diagonal */ + imlib_image_flip_diagonal(); + break; + default: + flip = flip ? 1 : 0; + return; + } +} + +void +rotateimage(void) +{ + rotation %= 4; + imlib_image_orientate(rotation); +} + +void +cleanupimage(void) +{ + if (image) { + imlib_free_image(); + image = NULL; + } +} + +void +drawimage(void) +{ + int width = 0, height = 0; + char *limg = NULL; + + if (!lines || hideimage) return; + + /* to prevent the image from being drawn multiple times */ + if (!needredraw) { + needredraw = 1; + return; + } + + if (sel && sel->image && strcmp(sel->image, limg ? limg : "")) { + if (longestedge) + loadimagecache(sel->image, &width, &height); + } else if ((!sel || !sel->image) && image) { + imlib_free_image(); + image = NULL; + } if (image && longestedge) { + + rotateimage(); + flipimage(); + + int leftmargin = imagegaps; + + if(mh != bh + height + imagegaps * 2) { + resizetoimageheight(height); + } + + if (!imageposition) { /* top mode = 0 */ + if (height > width) + width = height; + imlib_render_image_on_drawable(leftmargin+(imagewidth-width)/2, bh+imagegaps); + } else if (imageposition == 1) { /* bottom mode = 1 */ + if (height > width) + width = height; + imlib_render_image_on_drawable(leftmargin+(imagewidth-width)/2, mh-height-imagegaps); + } else if (imageposition == 2) { /* center mode = 2 */ + imlib_render_image_on_drawable(leftmargin+(imagewidth-width)/2, (mh-bh-height)/2+bh); + } else { + int minh = MIN(height, mh-bh-imagegaps*2); + imlib_render_image_on_drawable(leftmargin+(imagewidth-width)/2, (minh-height)/2+bh+imagegaps); + } + + + } if (sel) { + limg = sel->image; + } else { + limg = NULL; + } +} + +void +setimageopts(void) +{ + imlib_set_cache_size(8192 * 1024); + imlib_context_set_blend(1); + imlib_context_set_dither(1); + imlib_set_color_usage(128); + imlib_context_set_display(dpy); + imlib_context_set_visual(visual); + imlib_context_set_colormap(cmap); + imlib_context_set_drawable(win); +} + +void +createifnexist(const char *dir) +{ + if(access(dir, F_OK) == 0) + return; + + if(errno == EACCES) + fprintf(stderr, "spmenu: no access to create directory: %s\n", dir); + + if(mkdir(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1) + fprintf(stderr, "spmenu: failed to create directory: %s\n", dir); +} + +void +createifnexist_rec(const char *dir) +{ + char *buf, *s = (char*)dir, *bs; + + if(!(buf = malloc(strlen(s)+1))) + return; + + memset(buf, 0, strlen(s)+1); + + for(bs = buf; *s; ++s, ++bs) { + if(*s == '/' && *buf) + createifnexist(buf); + + *bs = *s; + } + + free(buf); +} + +void +loadimage(const char *file, int *width, int *height) +{ + image = imlib_load_image(file); + if (!image) + return; + + imlib_context_set_image(image); + + *width = imlib_image_get_width(); + *height = imlib_image_get_height(); +} + +void +scaleimage(int *width, int *height) +{ + int nwidth, nheight; + float aspect = 1.0f; + + if (imagewidth > *width) + aspect = (float)(*width)/imagewidth; + else + aspect = (float)imagewidth/(*width); + + nwidth = *width * aspect; + nheight = *height * aspect; + + if(nwidth == *width && nheight == *height) + return; + + image = imlib_create_cropped_scaled_image(0,0,*width,*height,nwidth,nheight); + + imlib_free_image(); + + if(!image) + return; + + imlib_context_set_image(image); + + *width = nwidth; + *height = nheight; + + return; +} + +void +loadimagecache(const char *file, int *width, int *height) +{ + int slen = 0, i; + unsigned char digest[MD5_DIGEST_LENGTH]; + char md5[MD5_DIGEST_LENGTH*2+1]; + char *xdg_cache, *home = NULL, *dsize, *buf = NULL; + struct passwd *pw = NULL; + + /* just load and don't store or try cache */ + if (longestedge > 256) { + loadimage(file, width, height); + if (image) + scaleimage(width, height); + return; + } + + if (generatecache) { + /* try find image from cache first */ + if(!(xdg_cache = getenv("XDG_CACHE_HOME"))) { + if(!(home = getenv("HOME")) && (pw = getpwuid(getuid()))) + home = pw->pw_dir; + if(!home) { + fprintf(stderr, "spmenu: could not find home directory"); + return; + } + } + + /* which cache do we try? */ + dsize = "normal"; + if (longestedge > 128) + dsize = "large"; + + slen = snprintf(NULL, 0, "file://%s", file)+1; + + if(!(buf = malloc(slen))) { + fprintf(stderr, "spmenu: out of memory"); + return; + } + + /* calculate md5 from path */ + sprintf(buf, "file://%s", file); + MD5((unsigned char*)buf, slen, digest); + + free(buf); + + for(i = 0; i < MD5_DIGEST_LENGTH; ++i) + sprintf(&md5[i*2], "%02x", (unsigned int)digest[i]); + + /* path for cached thumbnail */ + if (xdg_cache) + slen = snprintf(NULL, 0, "%s/thumbnails/%s/%s.png", xdg_cache, dsize, md5)+1; + else + slen = snprintf(NULL, 0, "%s/.thumbnails/%s/%s.png", home, dsize, md5)+1; + + if(!(buf = malloc(slen))) { + fprintf(stderr, "out of memory"); + return; + } + + if (xdg_cache) + sprintf(buf, "%s/thumbnails/%s/%s.png", xdg_cache, dsize, md5); + else + sprintf(buf, "%s/.thumbnails/%s/%s.png", home, dsize, md5); + + loadimage(buf, width, height); + + if (image && *width < imagewidth && *height < imageheight) { + imlib_free_image(); + image = NULL; + } else if(image && (*width > imagewidth || *height > imageheight)) { + scaleimage(width, height); + } + + /* we are done */ + if (image) { + free(buf); + return; + } + } + + /* we din't find anything from cache, or it was just wrong */ + loadimage(file, width, height); + scaleimage(width, height); + + if (!generatecache) return; + + imlib_image_set_format("png"); + + if (buf && generatecache) { + createifnexist_rec(buf); + imlib_save_image(buf); + free(buf); + } +} + +void +jumptoindex(unsigned int index) { + unsigned int i; + sel = curr = matches; + calcoffsets(); + for (i = 1; i < index; ++i) { + if(sel && sel->right && (sel = sel->right) == next) { + curr = next; + calcoffsets(); + } + } +} + +void +resizetoimageheight(int imageheight) +{ + int omh = mh, olines = lines; + lines = reallines; + + if (lines * bh < imageheight + imagegaps * 2) + lines = (imageheight + imagegaps * 2) / bh; + + mh = (lines + 1) * bh; + + if (mh - bh < imageheight + imagegaps * 2) + mh = imageheight + imagegaps * 2 + bh; + + if (!win || omh == mh) + return; + + XResizeWindow(dpy, win, mw, mh); + drw_resize(drw, mw, mh); + + if (olines != lines) { + struct item *item; + unsigned int i = 1; + + for (item = matches; item && item != sel; item = item->right) + ++i; + + jumptoindex(i); + } + + drawmenu(); +} diff --git a/libs/img-c.h b/libs/img-c.h new file mode 100644 index 0000000..14b545e --- /dev/null +++ b/libs/img-c.h @@ -0,0 +1,16 @@ +#include +#include +#include +#include + +static void setimagesize(int width, int height); +static void setimageopts(void); +static void cleanupimage(void); +static void drawimage(void); +static void rotateimage(void); +static void flipimage(void); +static void loadimage(const char *file, int *width, int *height); +static void loadimagecache(const char *file, int *width, int *height); +static void resizetoimageheight(int imageheight); + +static Imlib_Image image = NULL; diff --git a/libs/img.c b/libs/img.c index d3465c6..fbdf518 100644 --- a/libs/img.c +++ b/libs/img.c @@ -1,353 +1,3 @@ -void -setimagesize(int width, int height) -{ - #if !USEIMAGE - return; - #endif - - int oih = 0; - int oiw = 0; - - /* this makes sure we cannot scale the image up or down too much */ - if ((!image && height < imageheight) || (!image && width < imagewidth) || width > mw || hideimage) return; - - oih = imageheight; - oiw = imagewidth; - - imageheight = height; - imagewidth = width; - - drawimage(); - - needredraw = 0; - - if (!image) { - imageheight = oih; - imagewidth = oiw; - return; - } - - drawmenu(); -} - -void -flipimage(void) -{ - switch (flip) { - case 1: /* horizontal */ - imlib_image_flip_horizontal(); - break; - case 2: /* vertical */ - imlib_image_flip_vertical(); - break; - case 3: /* diagonal */ - imlib_image_flip_diagonal(); - break; - default: - flip = flip ? 1 : 0; - return; - } -} - -void -rotateimage(void) -{ - rotation %= 4; - imlib_image_orientate(rotation); -} - -void -cleanupimage(void) -{ - if (image) { - imlib_free_image(); - image = NULL; - } -} - -void -drawimage(void) -{ - int width = 0, height = 0; - char *limg = NULL; - - if (!lines || hideimage) return; - - /* to prevent the image from being drawn multiple times */ - if (!needredraw) { - needredraw = 1; - return; - } - - if (sel && sel->image && strcmp(sel->image, limg ? limg : "")) { - if (longestedge) - loadimagecache(sel->image, &width, &height); - } else if ((!sel || !sel->image) && image) { - imlib_free_image(); - image = NULL; - } if (image && longestedge) { - - rotateimage(); - flipimage(); - - int leftmargin = imagegaps; - - if(mh != bh + height + imagegaps * 2) { - resizetoimageheight(height); - } - - if (!imageposition) { /* top mode = 0 */ - if (height > width) - width = height; - imlib_render_image_on_drawable(leftmargin+(imagewidth-width)/2, bh+imagegaps); - } else if (imageposition == 1) { /* bottom mode = 1 */ - if (height > width) - width = height; - imlib_render_image_on_drawable(leftmargin+(imagewidth-width)/2, mh-height-imagegaps); - } else if (imageposition == 2) { /* center mode = 2 */ - imlib_render_image_on_drawable(leftmargin+(imagewidth-width)/2, (mh-bh-height)/2+bh); - } else { - int minh = MIN(height, mh-bh-imagegaps*2); - imlib_render_image_on_drawable(leftmargin+(imagewidth-width)/2, (minh-height)/2+bh+imagegaps); - } - - - } if (sel) { - limg = sel->image; - } else { - limg = NULL; - } -} - -void -setimageopts(void) -{ - imlib_set_cache_size(8192 * 1024); - imlib_context_set_blend(1); - imlib_context_set_dither(1); - imlib_set_color_usage(128); - imlib_context_set_display(dpy); - imlib_context_set_visual(visual); - imlib_context_set_colormap(cmap); - imlib_context_set_drawable(win); -} - -void -createifnexist(const char *dir) -{ - if(access(dir, F_OK) == 0) - return; - - if(errno == EACCES) - fprintf(stderr, "spmenu: no access to create directory: %s\n", dir); - - if(mkdir(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1) - fprintf(stderr, "spmenu: failed to create directory: %s\n", dir); -} - -void -createifnexist_rec(const char *dir) -{ - char *buf, *s = (char*)dir, *bs; - - if(!(buf = malloc(strlen(s)+1))) - return; - - memset(buf, 0, strlen(s)+1); - - for(bs = buf; *s; ++s, ++bs) { - if(*s == '/' && *buf) - createifnexist(buf); - - *bs = *s; - } - - free(buf); -} - -void -loadimage(const char *file, int *width, int *height) -{ - image = imlib_load_image(file); - if (!image) - return; - - imlib_context_set_image(image); - - *width = imlib_image_get_width(); - *height = imlib_image_get_height(); -} - -void -scaleimage(int *width, int *height) -{ - int nwidth, nheight; - float aspect = 1.0f; - - if (imagewidth > *width) - aspect = (float)(*width)/imagewidth; - else - aspect = (float)imagewidth/(*width); - - nwidth = *width * aspect; - nheight = *height * aspect; - - if(nwidth == *width && nheight == *height) - return; - - image = imlib_create_cropped_scaled_image(0,0,*width,*height,nwidth,nheight); - - imlib_free_image(); - - if(!image) - return; - - imlib_context_set_image(image); - - *width = nwidth; - *height = nheight; - - return; -} - -void -loadimagecache(const char *file, int *width, int *height) -{ - int slen = 0, i; - unsigned char digest[MD5_DIGEST_LENGTH]; - char md5[MD5_DIGEST_LENGTH*2+1]; - char *xdg_cache, *home = NULL, *dsize, *buf = NULL; - struct passwd *pw = NULL; - - /* just load and don't store or try cache */ - if (longestedge > 256) { - loadimage(file, width, height); - if (image) - scaleimage(width, height); - return; - } - - if (generatecache) { - /* try find image from cache first */ - if(!(xdg_cache = getenv("XDG_CACHE_HOME"))) { - if(!(home = getenv("HOME")) && (pw = getpwuid(getuid()))) - home = pw->pw_dir; - if(!home) { - fprintf(stderr, "spmenu: could not find home directory"); - return; - } - } - - /* which cache do we try? */ - dsize = "normal"; - if (longestedge > 128) - dsize = "large"; - - slen = snprintf(NULL, 0, "file://%s", file)+1; - - if(!(buf = malloc(slen))) { - fprintf(stderr, "spmenu: out of memory"); - return; - } - - /* calculate md5 from path */ - sprintf(buf, "file://%s", file); - MD5((unsigned char*)buf, slen, digest); - - free(buf); - - for(i = 0; i < MD5_DIGEST_LENGTH; ++i) - sprintf(&md5[i*2], "%02x", (unsigned int)digest[i]); - - /* path for cached thumbnail */ - if (xdg_cache) - slen = snprintf(NULL, 0, "%s/thumbnails/%s/%s.png", xdg_cache, dsize, md5)+1; - else - slen = snprintf(NULL, 0, "%s/.thumbnails/%s/%s.png", home, dsize, md5)+1; - - if(!(buf = malloc(slen))) { - fprintf(stderr, "out of memory"); - return; - } - - if (xdg_cache) - sprintf(buf, "%s/thumbnails/%s/%s.png", xdg_cache, dsize, md5); - else - sprintf(buf, "%s/.thumbnails/%s/%s.png", home, dsize, md5); - - loadimage(buf, width, height); - - if (image && *width < imagewidth && *height < imageheight) { - imlib_free_image(); - image = NULL; - } else if(image && (*width > imagewidth || *height > imageheight)) { - scaleimage(width, height); - } - - /* we are done */ - if (image) { - free(buf); - return; - } - } - - /* we din't find anything from cache, or it was just wrong */ - loadimage(file, width, height); - scaleimage(width, height); - - if (!generatecache) return; - - imlib_image_set_format("png"); - - if (buf && generatecache) { - createifnexist_rec(buf); - imlib_save_image(buf); - free(buf); - } -} - -void -jumptoindex(unsigned int index) { - unsigned int i; - sel = curr = matches; - calcoffsets(); - for (i = 1; i < index; ++i) { - if(sel && sel->right && (sel = sel->right) == next) { - curr = next; - calcoffsets(); - } - } -} - -void -resizetoimageheight(int imageheight) -{ - int omh = mh, olines = lines; - lines = reallines; - - if (lines * bh < imageheight + imagegaps * 2) - lines = (imageheight + imagegaps * 2) / bh; - - mh = (lines + 1) * bh; - - if (mh - bh < imageheight + imagegaps * 2) - mh = imageheight + imagegaps * 2 + bh; - - if (!win || omh == mh) - return; - - XResizeWindow(dpy, win, mw, mh); - drw_resize(drw, mw, mh); - - if (olines != lines) { - struct item *item; - unsigned int i = 1; - - for (item = matches; item && item != sel; item = item->right) - ++i; - - jumptoindex(i); - } - - drawmenu(); -} +#if USEIMAGE +#include "img-c.c" +#endif diff --git a/libs/img.h b/libs/img.h index 14b545e..0b13e85 100644 --- a/libs/img.h +++ b/libs/img.h @@ -1,16 +1,3 @@ -#include -#include -#include -#include - -static void setimagesize(int width, int height); -static void setimageopts(void); -static void cleanupimage(void); -static void drawimage(void); -static void rotateimage(void); -static void flipimage(void); -static void loadimage(const char *file, int *width, int *height); -static void loadimagecache(const char *file, int *width, int *height); -static void resizetoimageheight(int imageheight); - -static Imlib_Image image = NULL; +#if USEIMAGE +#include "img-c.h" +#endif diff --git a/libs/key.c b/libs/key.c index 33b8a02..3110b6c 100644 --- a/libs/key.c +++ b/libs/key.c @@ -56,3 +56,21 @@ keypress(XEvent *e) } } } + +void +grabkeyboard(void) +{ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; + int i; + + if (embed || managed) + return; + /* try to grab keyboard, we may have to wait for another process to ungrab */ + for (i = 0; i < 1000; i++) { + if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, + GrabModeAsync, CurrentTime) == GrabSuccess) + return; + nanosleep(&ts, NULL); + } + die("cannot grab keyboard"); +} diff --git a/libs/key.h b/libs/key.h index 9d1c36f..ad5b3b9 100644 --- a/libs/key.h +++ b/libs/key.h @@ -1,2 +1,3 @@ static void updatenumlockmask(void); static void keypress(XEvent *e); +static void grabkeyboard(void); diff --git a/libs/match.c b/libs/match.c new file mode 100644 index 0000000..d68c54c --- /dev/null +++ b/libs/match.c @@ -0,0 +1,173 @@ +void +fuzzymatch(void) +{ + struct item *it; + struct item **fuzzymatches = NULL; + struct item *lhpprefix, *hpprefixend; + lhpprefix = hpprefixend = 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 */ + if (sortmatches) 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]) { + + if (sortmatches && it->hp) + appenditem(it, &lhpprefix, &hpprefixend); + + appenditem(it, &matches, &matchend); + } + free(fuzzymatches); + } + + if (lhpprefix) { + hpprefixend->right = matches; + matches = lhpprefix; + } + + curr = sel = matches; + + for (i = 0; i < preselected; i++) { + if (sel && sel->right && (sel = sel->right) == next) { + curr = next; + calcoffsets(); + } + } + + calcoffsets(); +} + +void +match(void) +{ + if (fuzzy) { + fuzzymatch(); + return; + } + static char **tokv = NULL; + static int tokn = 0; + + char buf[sizeof text], *s; + int i, tokc = 0; + size_t len, textsize; + struct item *item, *lhpprefix, *lprefix, *lsubstr, *hpprefixend, *prefixend, *substrend; + + + 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 %u bytes:", tokn * sizeof *tokv); + len = tokc ? strlen(tokv[0]) : 0; + + matches = lhpprefix = lprefix = lsubstr = matchend = hpprefixend = prefixend = substrend = NULL; + textsize = strlen(text) + 1; + for (item = items; item && item->text; item++) { + for (i = 0; i < tokc; i++) + if (!fstrstr(item->text, tokv[i])) + break; + if (i != tokc) /* not all tokens match */ + continue; + if (!sortmatches) + appenditem(item, &matches, &matchend); + else { + /* exact matches go first, then prefixes with high priority, then prefixes, then substrings */ + if (item->hp && !fstrncmp(tokv[0], item->text, len)) + appenditem(item, &lhpprefix, &hpprefixend); + else 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 (lhpprefix) { + if (matches) { + matchend->right = lhpprefix; + lhpprefix->left = matchend; + } else + matches = lhpprefix; + matchend = hpprefixend; + } + if (lprefix) { + if (matches) { + matchend->right = lprefix; + lprefix->left = matchend; + } else + matches = lprefix; + matchend = prefixend; + } + if (lsubstr) { + if (matches) { + matchend->right = lsubstr; + lsubstr->left = matchend; + } else + matches = lsubstr; + matchend = substrend; + } + curr = sel = matches; + calcoffsets(); +} + +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; +} diff --git a/libs/match.h b/libs/match.h new file mode 100644 index 0000000..00a1472 --- /dev/null +++ b/libs/match.h @@ -0,0 +1,3 @@ +static void fuzzymatch(void); +static void match(void); +static int compare_distance(const void *a, const void *b); diff --git a/libs/rtl-c.c b/libs/rtl-c.c new file mode 100644 index 0000000..92008c8 --- /dev/null +++ b/libs/rtl-c.c @@ -0,0 +1,23 @@ +void +apply_fribidi(char *str) +{ + FriBidiStrIndex len = strlen(str); + FriBidiChar logical[BUFSIZ]; + FriBidiChar visual[BUFSIZ]; + FriBidiParType base = FRIBIDI_PAR_ON; + FriBidiCharSet charset; + fribidi_boolean result; + + fribidi_text[0] = 0; + if (len>0) { + charset = fribidi_parse_charset("UTF-8"); + len = fribidi_charset_to_unicode(charset, str, len, logical); + result = fribidi_log2vis(logical, len, &base, visual, NULL, NULL, NULL); + len = fribidi_unicode_to_charset(charset, visual, len, fribidi_text); + } + + if (result) + return; + else + return; +} diff --git a/libs/rtl-c.h b/libs/rtl-c.h new file mode 100644 index 0000000..cc8008f --- /dev/null +++ b/libs/rtl-c.h @@ -0,0 +1,4 @@ +static char fribidi_text[BUFSIZ] = ""; + +/* functions */ +static void apply_fribidi(char *str); diff --git a/libs/rtl-none.c b/libs/rtl-none.c deleted file mode 100644 index e983944..0000000 --- a/libs/rtl-none.c +++ /dev/null @@ -1,5 +0,0 @@ -void -apply_fribidi(char *str) -{ - return; -} diff --git a/libs/rtl-none.h b/libs/rtl-none.h deleted file mode 100644 index b3eb2ba..0000000 --- a/libs/rtl-none.h +++ /dev/null @@ -1 +0,0 @@ -static void apply_fribidi(char *str); diff --git a/libs/rtl.c b/libs/rtl.c index 92008c8..18e3148 100644 --- a/libs/rtl.c +++ b/libs/rtl.c @@ -1,23 +1,3 @@ -void -apply_fribidi(char *str) -{ - FriBidiStrIndex len = strlen(str); - FriBidiChar logical[BUFSIZ]; - FriBidiChar visual[BUFSIZ]; - FriBidiParType base = FRIBIDI_PAR_ON; - FriBidiCharSet charset; - fribidi_boolean result; - - fribidi_text[0] = 0; - if (len>0) { - charset = fribidi_parse_charset("UTF-8"); - len = fribidi_charset_to_unicode(charset, str, len, logical); - result = fribidi_log2vis(logical, len, &base, visual, NULL, NULL, NULL); - len = fribidi_unicode_to_charset(charset, visual, len, fribidi_text); - } - - if (result) - return; - else - return; -} +#if USERTL +#include "rtl-c.c" +#endif diff --git a/libs/rtl.h b/libs/rtl.h index cc8008f..65d2efe 100644 --- a/libs/rtl.h +++ b/libs/rtl.h @@ -1,4 +1,6 @@ +#if USERTL +#include "rtl-c.h" +#else static char fribidi_text[BUFSIZ] = ""; - -/* functions */ static void apply_fribidi(char *str); +#endif diff --git a/options.h b/options.h index 6880425..2d92d3b 100644 --- a/options.h +++ b/options.h @@ -30,12 +30,10 @@ static char normtext[] = "Normal"; /* Text to display for normal static char instext[] = "Insert"; /* Text to display for insert mode */ /* Window border options */ -static int borderwidth = 0; /* Width of the border */ +static int borderwidth = 2; /* Width of the border */ /* Font options */ -static char *font[] = { - "Noto Sans Mono 8", /* Font to draw text and Pango markup with. */ -}; +static char *font[] = { "Noto Sans Mono 8" }; /* Font to draw text and Pango markup with. */ /* Symbol options */ static char *leftarrow = "<"; /* Left arrow, used to indicate you can move to the left */ diff --git a/spmenu.1 b/spmenu.1 index bafd25f..4fa159e 100644 --- a/spmenu.1 +++ b/spmenu.1 @@ -153,6 +153,10 @@ spmenu -si - Show image .IP \[bu] 2 spmenu -xrdb - Load .Xresources on runtime .IP \[bu] 2 +spmenu -nxrdb - Don\[cq]t load .Xresources on runtime +.IP \[bu] 2 +spmenu -m monitor - Specify a monitor to run spmenu on +.IP \[bu] 2 spmenu -w window id - Embed spmenu inside window id .IP \[bu] 2 spmenu -H hist file - Specify a path to save the history to diff --git a/spmenu.c b/spmenu.c index 6fc6bd1..7ca156e 100644 --- a/spmenu.c +++ b/spmenu.c @@ -174,7 +174,6 @@ static int imageg = 0; static int isrtl = 1; #else static int isrtl = 0; -static char fribidi_text[] = ""; #endif static Atom clip, utf8, types, dock; @@ -208,6 +207,7 @@ static void cleanup(void); static void navigatehistfile(int dir); static void grabfocus(void); static void pastesel(void); +static void appenditem(struct item *item, struct item **list, struct item **last); static int max_textw(void); /* user configuration */ @@ -221,17 +221,13 @@ 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; -#if USEIMAGE +// include functions #include "libs/img.h" #include "libs/img.c" -#endif -#if USERTL #include "libs/rtl.h" #include "libs/rtl.c" -#else -#include "libs/rtl-none.h" -#include "libs/rtl-none.c" -#endif +#include "libs/match.h" +#include "libs/match.c" #include "libs/event.h" #include "libs/event.c" #include "libs/key.c" @@ -380,199 +376,6 @@ grabfocus(void) die("cannot grab focus"); } -void -grabkeyboard(void) -{ - struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; - int i; - - if (embed || managed) - return; - /* try to grab keyboard, we may have to wait for another process to ungrab */ - for (i = 0; i < 1000; i++) { - if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, - GrabModeAsync, CurrentTime) == GrabSuccess) - return; - nanosleep(&ts, NULL); - } - 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; - struct item *lhpprefix, *hpprefixend; - lhpprefix = hpprefixend = 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 */ - if (sortmatches) 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]) { - - if (sortmatches && it->hp) - appenditem(it, &lhpprefix, &hpprefixend); - - appenditem(it, &matches, &matchend); - } - free(fuzzymatches); - } - - if (lhpprefix) { - hpprefixend->right = matches; - matches = lhpprefix; - } - - curr = sel = matches; - - for (i = 0; i < preselected; i++) { - if (sel && sel->right && (sel = sel->right) == next) { - curr = next; - calcoffsets(); - } - } - - calcoffsets(); -} - -void -match(void) -{ - if (fuzzy) { - fuzzymatch(); - return; - } - static char **tokv = NULL; - static int tokn = 0; - - char buf[sizeof text], *s; - int i, tokc = 0; - size_t len, textsize; - struct item *item, *lhpprefix, *lprefix, *lsubstr, *hpprefixend, *prefixend, *substrend; - - - 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 %u bytes:", tokn * sizeof *tokv); - len = tokc ? strlen(tokv[0]) : 0; - - matches = lhpprefix = lprefix = lsubstr = matchend = hpprefixend = prefixend = substrend = NULL; - textsize = strlen(text) + 1; - for (item = items; item && item->text; item++) { - for (i = 0; i < tokc; i++) - if (!fstrstr(item->text, tokv[i])) - break; - if (i != tokc) /* not all tokens match */ - continue; - if (!sortmatches) - appenditem(item, &matches, &matchend); - else { - /* exact matches go first, then prefixes with high priority, then prefixes, then substrings */ - if (item->hp && !fstrncmp(tokv[0], item->text, len)) - appenditem(item, &lhpprefix, &hpprefixend); - else 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 (lhpprefix) { - if (matches) { - matchend->right = lhpprefix; - lhpprefix->left = matchend; - } else - matches = lhpprefix; - matchend = hpprefixend; - } - if (lprefix) { - if (matches) { - matchend->right = lprefix; - lprefix->left = matchend; - } else - matches = lprefix; - matchend = prefixend; - } - if (lsubstr) { - if (matches) { - matchend->right = lsubstr; - lsubstr->left = matchend; - } else - matches = lsubstr; - matchend = substrend; - } - curr = sel = matches; - calcoffsets(); -} - void insert(const char *str, ssize_t n) {