123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438 |
- /*
- * wmfs2 by Martin Duquesnoy <xorg62@gmail.com> { for(i = 2011; i < 2111; ++i) ©(i); }
- * For license, see COPYING.
- */
- #include <string.h>
- #include <dirent.h>
- #include <sys/stat.h>
- #include <X11/Xutil.h>
- #include "wmfs.h"
- #include "event.h"
- #include "util.h"
- #include "infobar.h"
- #include "config.h"
- static int
- qsort_string_compare(const void * a, const void * b)
- {
- return (strcmp(*(char **)a, *(char **)b));
- }
- static char **
- complete_on_command(char *start)
- {
- struct dirent *content;
- DIR *dir;
- char **paths, *path, *p, **namelist = NULL;
- int i, count;
- if(!(path = getenv("PATH")) || !start)
- return NULL;
- /* split PATH into paths */
- path = p = xstrdup(path);
- for(count = 1, p = path; strchr(p, ':'); ++p, ++count);
- paths = xcalloc(count, sizeof(*paths));
- for(paths[0] = p = path, count = 1; (p = strchr(p, ':')); ++p, ++count)
- {
- paths[count] = p + 1;
- *p = '\0';
- }
- paths[count] = NULL;
- /* recursively open PATH */
- for(i = count = 0; paths[i]; ++i)
- {
- if(!(dir = opendir(paths[i])))
- continue;
- while((content = readdir(dir)))
- {
- if(strncmp(content->d_name, ".", 1)
- && !strncmp(content->d_name, start, strlen(start)))
- {
- namelist = xrealloc(namelist, ++count, sizeof(*namelist));
- namelist[count - 1] = xstrdup(content->d_name + strlen(start));
- }
- }
- closedir(dir);
- }
- if(count)
- {
- qsort(namelist, count, sizeof(char *), qsort_string_compare);
- namelist = xrealloc(namelist, ++count, sizeof(*namelist));
- namelist[count - 1] = NULL;
- }
- free(paths);
- free(path);
- return namelist;
- }
- /*
- * Complete a filename or directory name.
- * works like complete_on_command.
- */
- static char **
- complete_on_files(char *start)
- {
- struct dirent *content = NULL;
- struct stat st;
- DIR *dir;
- char *home, *path, *dirname = NULL;
- char **namelist = NULL, *filepath, *p = start;
- int count = 0;
- /*
- * Search the directory to open and set
- * the beginning of file to complete on pointer 'p'.
- */
- if(*p == '\0' || !strrchr(p, '/'))
- path = xstrdup(".");
- else
- {
- if(!(home = getenv("HOME")))
- return NULL;
- /* remplace ~ by $HOME in dirname */
- if(!strncmp(p, "~/", 2) && home)
- xasprintf(&dirname, "%s%s", home, p+1);
- else
- dirname = xstrdup(p);
- /* Set p to filename to be complete
- * and path the directory containing the file
- * /foooooo/baaaaaar/somethinglikethis<tab>
- * <---- path - ---><------- p ------>
- */
- p = strrchr(dirname, '/');
- if(p != dirname)
- {
- *(p++) = '\0';
- path = xstrdup(dirname);
- }
- else
- {
- path = xstrdup("/");
- p++;
- }
- }
- if((dir = opendir(path)))
- {
- while((content = readdir(dir)))
- {
- if(!strcmp(content->d_name, ".")
- || !strcmp(content->d_name, "..")
- || strncmp(content->d_name, p, strlen(p)))
- continue;
- /* If it's a directory append '/' to the completion */
- xasprintf(&filepath, "%s/%s", path, content->d_name);
- if(filepath && stat(filepath, &st) != -1)
- {
- namelist = xrealloc(namelist, ++count, sizeof(*namelist));
- if(S_ISDIR(st.st_mode))
- xasprintf(&namelist[count - 1], "%s/", content->d_name + strlen(p));
- else
- namelist[count - 1] = xstrdup(content->d_name + strlen(p));
- }
- else
- warnl("%s", filepath);
- free(filepath);
- }
- closedir(dir);
- }
- if(count)
- {
- namelist = xrealloc(namelist, ++count, sizeof(*namelist));
- namelist[count - 1] = NULL;
- }
- free(dirname);
- free(path);
- return namelist;
- }
- static void
- complete_cache_free(struct launcher_ccache *cache)
- {
- int i;
- /* release memory */
- free(cache->start);
- if(cache->namelist)
- {
- for(i = 0; cache->namelist[i]; i++)
- free(cache->namelist[i]);
- free(cache->namelist);
- }
- /* init */
- cache->hits = 0;
- cache->start = NULL;
- cache->namelist = NULL;
- }
- static char *
- complete(struct launcher_ccache *cache, char *start)
- {
- char *p = NULL, *comp = NULL;
- if(!start || !cache)
- return NULL;
- if((p = strrchr(start, ' ')))
- p++;
- else
- p = start;
- if(cache->start && !strcmp(cache->start, start))
- {
- if(cache->namelist && !cache->namelist[cache->hits])
- cache->hits = 0;
- }
- else
- {
- complete_cache_free(cache);
- cache->start = xstrdup(start);
- if(p == start)
- cache->namelist = complete_on_command(p);
- else
- cache->namelist = complete_on_files(p);
- }
- if(cache->namelist && cache->namelist[cache->hits])
- comp = cache->namelist[cache->hits];
- return comp;
- }
- #define LAUNCHER_INIT_ELEM(width) \
- SLIST_FOREACH(ib, &W->screen->infobars, next) \
- { \
- TAILQ_FOREACH(e, &ib->elements, next) \
- if(e->type == ElemLauncher) \
- { \
- e->geo.w = width; \
- e->data = data; \
- } \
- infobar_elem_reinit(ib); \
- }
- static void
- launcher_process(struct launcher *l)
- {
- struct infobar *ib;
- struct element *e;
- struct launcher_ccache cache = {NULL, NULL, 0};
- bool loop = true, found = false, lastwastab = false;
- char tmpbuf[512] = { 0 }, buf[512] = { 0 };
- char tmp[32] = { 0 };
- char *p, *data, *arg, *end, *cmd = xstrdup(l->command);
- int i, pos = 0, histpos = 0;
- void (*func)(Uicb);
- XEvent ev;
- KeySym ks;
- W->flags |= WMFS_LAUNCHER;
- /* Prepare elements */
- xasprintf(&data, "%s ", l->prompt);
- LAUNCHER_INIT_ELEM(l->width);
- XGrabKeyboard(W->dpy, W->root, true, GrabModeAsync, GrabModeAsync, CurrentTime);
- while(loop)
- {
- XNextEvent(W->dpy, &ev);
- if(ev.type != KeyPress)
- {
- EVENT_HANDLE(&ev);
- continue;
- }
- /* Get pressed key */
- XLookupString(&ev.xkey, tmp, sizeof(tmp), &ks, 0);
- /* Check Ctrl-c / Ctrl-d */
- if(ev.xkey.state & ControlMask)
- {
- switch(ks)
- {
- case XK_c:
- case XK_d:
- ks = XK_Escape;
- break;
- case XK_p:
- ks = XK_Up;
- break;
- case XK_n:
- ks = XK_Down;
- break;
- }
- }
- /* Check if there is a keypad */
- if(IsKeypadKey(ks) && ks == XK_KP_Enter)
- ks = XK_Return;
- /* Manage pressed keys */
- switch(ks)
- {
- case XK_Up:
- if(l->nhisto)
- {
- if(histpos >= (int)l->nhisto)
- histpos = 0;
- strncpy(buf, l->histo[l->nhisto - ++histpos], sizeof(buf));
- pos = strlen(buf);
- }
- break;
- case XK_Down:
- if(l->nhisto && histpos > 0 && histpos < (int)l->nhisto)
- {
- strncpy(buf, l->histo[l->nhisto - --histpos], sizeof(buf));
- pos = strlen(buf);
- }
- break;
- case XK_Return:
- /* Get function name only, if cmds are added in command */
- arg = NULL;
- if((p = strchr(cmd, ' ')))
- {
- *p = '\0';
- xasprintf(&arg, "%s %s", p + 1, buf);
- }
- if((func = uicb_name_func(cmd)))
- {
- if(arg)
- {
- func(arg);
- free(arg);
- }
- else
- func(buf);
- }
- /* Histo */
- if(l->nhisto + 1 > HISTOLEN)
- {
- for(i = l->nhisto - 1; i > 1; --i)
- strncpy(l->histo[i], l->histo[i - 1], sizeof(l->histo[i]));
- l->nhisto = 0;
- }
- /* Store in histo array */
- strncpy(l->histo[l->nhisto++], buf, sizeof(buf));
- loop = false;
- break;
- case XK_Escape:
- loop = false;
- break;
- /* Completion */
- case XK_Tab:
- buf[pos] = '\0';
- if(lastwastab)
- ++cache.hits;
- else
- {
- cache.hits = 0;
- strncpy(tmpbuf, buf, sizeof(tmpbuf));
- }
- if(pos && (end = complete(&cache, tmpbuf)))
- {
- strncpy(buf, tmpbuf, sizeof(buf));
- strncat(buf, end, sizeof(buf));
- found = true;
- }
- lastwastab = true;
- /* start a new round of tabbing */
- if(!found)
- cache.hits = 0;
- pos = strlen(buf);
- break;
- case XK_BackSpace:
- lastwastab = false;
- if(pos)
- buf[--pos] = '\0';
- break;
- default:
- lastwastab = false;
- strncat(buf, tmp, sizeof(tmp));
- ++pos;
- break;
- }
- free(data);
- xasprintf(&data, "%s %s", l->prompt, buf);
- /* Update EVERY launcher element of the screen */
- SLIST_FOREACH(ib, &W->screen->infobars, next)
- {
- TAILQ_FOREACH(e, &ib->elements, next)
- {
- if(e->type != ElemLauncher)
- continue;
- e->data = data;
- e->func_update(e);
- }
- }
- }
- XUngrabKeyboard(W->dpy, CurrentTime);
- complete_cache_free(&cache);
- free(cmd);
- free(data);
- /* 'Close' launcher elements */
- W->flags ^= WMFS_LAUNCHER;
- data = NULL;
- LAUNCHER_INIT_ELEM(1);
- }
- void
- uicb_launcher(Uicb cmd)
- {
- struct launcher *l;
- if(!cmd)
- return;
- SLIST_FOREACH(l, &W->h.launcher, next)
- if(!strcmp(l->name, cmd))
- {
- launcher_process(l);
- break;
- }
- }
|