diff options
| -rw-r--r-- | Makefile | 15 | ||||
| -rw-r--r-- | common.c | 129 | ||||
| -rw-r--r-- | dinput.c | 230 | ||||
| -rw-r--r-- | dmenu.c | 251 | ||||
| -rw-r--r-- | dmenu.h | 30 |
5 files changed, 215 insertions, 440 deletions
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | include config.mk | 4 | include config.mk |
| 5 | 5 | ||
| 6 | SRC = dinput.c dmenu.c common.c | 6 | SRC = dmenu.c |
| 7 | OBJ = ${SRC:.c=.o} | 7 | OBJ = ${SRC:.c=.o} |
| 8 | 8 | ||
| 9 | all: options dinput dmenu | 9 | all: options dinput dmenu |
| @@ -24,17 +24,13 @@ config.h: | |||
| 24 | @echo creating $@ from config.def.h | 24 | @echo creating $@ from config.def.h |
| 25 | @cp config.def.h $@ | 25 | @cp config.def.h $@ |
| 26 | 26 | ||
| 27 | dinput: dinput.o common.o | 27 | dmenu: ${OBJ} |
| 28 | @echo CC -o $@ | ||
| 29 | @${CC} -o $@ $+ ${LDFLAGS} | ||
| 30 | |||
| 31 | dmenu: dmenu.o common.o | ||
| 32 | @echo CC -o $@ | 28 | @echo CC -o $@ |
| 33 | @${CC} -o $@ $+ ${LDFLAGS} | 29 | @${CC} -o $@ $+ ${LDFLAGS} |
| 34 | 30 | ||
| 35 | clean: | 31 | clean: |
| 36 | @echo cleaning | 32 | @echo cleaning |
| 37 | @rm -f dinput dmenu ${OBJ} dmenu-${VERSION}.tar.gz | 33 | @rm -f dmenu ${OBJ} dmenu-${VERSION}.tar.gz |
| 38 | 34 | ||
| 39 | dist: clean | 35 | dist: clean |
| 40 | @echo creating dist tarball | 36 | @echo creating dist tarball |
| @@ -47,8 +43,7 @@ dist: clean | |||
| 47 | install: all | 43 | install: all |
| 48 | @echo installing executable file to ${DESTDIR}${PREFIX}/bin | 44 | @echo installing executable file to ${DESTDIR}${PREFIX}/bin |
| 49 | @mkdir -p ${DESTDIR}${PREFIX}/bin | 45 | @mkdir -p ${DESTDIR}${PREFIX}/bin |
| 50 | @cp -f dinput dmenu dmenu_path dmenu_run ${DESTDIR}${PREFIX}/bin | 46 | @cp -f dmenu dmenu_path dmenu_run ${DESTDIR}${PREFIX}/bin |
| 51 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dinput | ||
| 52 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu | 47 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu |
| 53 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path | 48 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path |
| 54 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run | 49 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run |
| @@ -60,7 +55,7 @@ install: all | |||
| 60 | uninstall: | 55 | uninstall: |
| 61 | @echo removing executable file from ${DESTDIR}${PREFIX}/bin | 56 | @echo removing executable file from ${DESTDIR}${PREFIX}/bin |
| 62 | @rm -f ${DESTDIR}${PREFIX}/bin/dmenu ${DESTDIR}${PREFIX}/bin/dmenu_path | 57 | @rm -f ${DESTDIR}${PREFIX}/bin/dmenu ${DESTDIR}${PREFIX}/bin/dmenu_path |
| 63 | @rm -f ${DESTDIR}${PREFIX}/bin/dinput ${DESTDIR}${PREFIX}/bin/dmenu_run | 58 | @rm -f ${DESTDIR}${PREFIX}/bin/dmenu_run |
| 64 | @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 | 59 | @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 |
| 65 | @rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1 | 60 | @rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1 |
| 66 | 61 | ||
diff --git a/common.c b/common.c deleted file mode 100644 index 2d19aee..0000000 --- a/common.c +++ /dev/null | |||
| @@ -1,129 +0,0 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | ||
| 2 | #include <stdlib.h> | ||
| 3 | #include <string.h> | ||
| 4 | #include <unistd.h> | ||
| 5 | #include <X11/keysym.h> | ||
| 6 | #ifdef XINERAMA | ||
| 7 | #include <X11/extensions/Xinerama.h> | ||
| 8 | #endif | ||
| 9 | #include "dmenu.h" | ||
| 10 | |||
| 11 | /* variables */ | ||
| 12 | char *prompt = NULL; | ||
| 13 | char text[4096] = ""; | ||
| 14 | int promptw = 0; | ||
| 15 | int screen; | ||
| 16 | unsigned int numlockmask = 0; | ||
| 17 | unsigned int mw, mh; | ||
| 18 | unsigned long normcol[ColLast]; | ||
| 19 | unsigned long selcol[ColLast]; | ||
| 20 | Bool topbar = True; | ||
| 21 | DC dc; | ||
| 22 | Display *dpy; | ||
| 23 | Window win, root; | ||
| 24 | |||
| 25 | void | ||
| 26 | grabkeyboard(void) { | ||
| 27 | unsigned int len; | ||
| 28 | |||
| 29 | for(len = 1000; len; len--) { | ||
| 30 | if(XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime) | ||
| 31 | == GrabSuccess) | ||
| 32 | return; | ||
| 33 | usleep(1000); | ||
| 34 | } | ||
| 35 | exit(EXIT_FAILURE); | ||
| 36 | } | ||
| 37 | |||
| 38 | void | ||
| 39 | run(void) { | ||
| 40 | XEvent ev; | ||
| 41 | |||
| 42 | /* main event loop */ | ||
| 43 | XSync(dpy, False); | ||
| 44 | while(!XNextEvent(dpy, &ev)) | ||
| 45 | switch(ev.type) { | ||
| 46 | case KeyPress: | ||
| 47 | kpress(&ev.xkey); | ||
| 48 | break; | ||
| 49 | case Expose: | ||
| 50 | if(ev.xexpose.count == 0) | ||
| 51 | drawbar(); | ||
| 52 | break; | ||
| 53 | case VisibilityNotify: | ||
| 54 | if(ev.xvisibility.state != VisibilityUnobscured) | ||
| 55 | XRaiseWindow(dpy, win); | ||
| 56 | break; | ||
| 57 | } | ||
| 58 | exit(EXIT_FAILURE); | ||
| 59 | } | ||
| 60 | |||
| 61 | void | ||
| 62 | setup(unsigned int lines) { | ||
| 63 | int i, j, x, y; | ||
| 64 | #if XINERAMA | ||
| 65 | int n; | ||
| 66 | XineramaScreenInfo *info = NULL; | ||
| 67 | #endif | ||
| 68 | XModifierKeymap *modmap; | ||
| 69 | XSetWindowAttributes wa; | ||
| 70 | |||
| 71 | /* init modifier map */ | ||
| 72 | modmap = XGetModifierMapping(dpy); | ||
| 73 | for(i = 0; i < 8; i++) | ||
| 74 | for(j = 0; j < modmap->max_keypermod; j++) { | ||
| 75 | if(modmap->modifiermap[i * modmap->max_keypermod + j] | ||
| 76 | == XKeysymToKeycode(dpy, XK_Num_Lock)) | ||
| 77 | numlockmask = (1 << i); | ||
| 78 | } | ||
| 79 | XFreeModifiermap(modmap); | ||
| 80 | |||
| 81 | dc.dpy = dpy; | ||
| 82 | normcol[ColBG] = getcolor(&dc, normbgcolor); | ||
| 83 | normcol[ColFG] = getcolor(&dc, normfgcolor); | ||
| 84 | selcol[ColBG] = getcolor(&dc, selbgcolor); | ||
| 85 | selcol[ColFG] = getcolor(&dc, selfgcolor); | ||
| 86 | initfont(&dc, font); | ||
| 87 | |||
| 88 | /* input window */ | ||
| 89 | wa.override_redirect = True; | ||
| 90 | wa.background_pixmap = ParentRelative; | ||
| 91 | wa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; | ||
| 92 | |||
| 93 | /* input window geometry */ | ||
| 94 | mh = (dc.font.height + 2) * (lines + 1); | ||
| 95 | #if XINERAMA | ||
| 96 | if(XineramaIsActive(dpy) && (info = XineramaQueryScreens(dpy, &n))) { | ||
| 97 | i = 0; | ||
| 98 | if(n > 1) { | ||
| 99 | int di; | ||
| 100 | unsigned int dui; | ||
| 101 | Window dummy; | ||
| 102 | if(XQueryPointer(dpy, root, &dummy, &dummy, &x, &y, &di, &di, &dui)) | ||
| 103 | for(i = 0; i < n; i++) | ||
| 104 | if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height)) | ||
| 105 | break; | ||
| 106 | } | ||
| 107 | x = info[i].x_org; | ||
| 108 | y = topbar ? info[i].y_org : info[i].y_org + info[i].height - mh; | ||
| 109 | mw = info[i].width; | ||
| 110 | XFree(info); | ||
| 111 | } | ||
| 112 | else | ||
| 113 | #endif | ||
| 114 | { | ||
| 115 | x = 0; | ||
| 116 | y = topbar ? 0 : DisplayHeight(dpy, screen) - mh; | ||
| 117 | mw = DisplayWidth(dpy, screen); | ||
| 118 | } | ||
| 119 | |||
| 120 | win = XCreateWindow(dpy, root, x, y, mw, mh, 0, | ||
| 121 | DefaultDepth(dpy, screen), CopyFromParent, | ||
| 122 | DefaultVisual(dpy, screen), | ||
| 123 | CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); | ||
| 124 | |||
| 125 | setupdraw(&dc, win); | ||
| 126 | if(prompt) | ||
| 127 | promptw = MIN(textw(&dc, prompt), mw / 5); | ||
| 128 | XMapRaised(dpy, win); | ||
| 129 | } | ||
diff --git a/dinput.c b/dinput.c deleted file mode 100644 index 4bbc7bc..0000000 --- a/dinput.c +++ /dev/null | |||
| @@ -1,230 +0,0 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | ||
| 2 | #include <ctype.h> | ||
| 3 | #include <locale.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | #include <stdlib.h> | ||
| 6 | #include <string.h> | ||
| 7 | #include <X11/keysym.h> | ||
| 8 | #include <X11/Xlib.h> | ||
| 9 | #include <X11/Xutil.h> | ||
| 10 | #include "dmenu.h" | ||
| 11 | |||
| 12 | /* forward declarations */ | ||
| 13 | static void cleanup(void); | ||
| 14 | |||
| 15 | /* variables */ | ||
| 16 | static size_t cursor = 0; | ||
| 17 | |||
| 18 | void | ||
| 19 | cleanup(void) { | ||
| 20 | cleanupdraw(&dc); | ||
| 21 | XDestroyWindow(dpy, win); | ||
| 22 | XUngrabKeyboard(dpy, CurrentTime); | ||
| 23 | XCloseDisplay(dpy); | ||
| 24 | } | ||
| 25 | |||
| 26 | void | ||
| 27 | drawbar(void) | ||
| 28 | { | ||
| 29 | dc.x = 0; | ||
| 30 | dc.y = 0; | ||
| 31 | dc.w = mw; | ||
| 32 | dc.h = mh; | ||
| 33 | drawbox(&dc, normcol); | ||
| 34 | /* print prompt? */ | ||
| 35 | if(prompt) { | ||
| 36 | dc.w = promptw; | ||
| 37 | drawbox(&dc, selcol); | ||
| 38 | drawtext(&dc, prompt, selcol); | ||
| 39 | dc.x += dc.w; | ||
| 40 | } | ||
| 41 | dc.w = mw - dc.x; | ||
| 42 | drawtext(&dc, text, normcol); | ||
| 43 | drawline(&dc, textnw(&dc, text, cursor) + dc.font.height/2, 2, 1, | ||
| 44 | dc.font.height-2, normcol); | ||
| 45 | commitdraw(&dc, win); | ||
| 46 | } | ||
| 47 | |||
| 48 | void | ||
| 49 | kpress(XKeyEvent *e) { | ||
| 50 | char buf[sizeof text]; | ||
| 51 | int num; | ||
| 52 | unsigned int i, len; | ||
| 53 | KeySym ksym; | ||
| 54 | |||
| 55 | len = strlen(text); | ||
| 56 | num = XLookupString(e, buf, sizeof buf, &ksym, NULL); | ||
| 57 | if(ksym == XK_KP_Enter) | ||
| 58 | ksym = XK_Return; | ||
| 59 | else if(ksym >= XK_KP_0 && ksym <= XK_KP_9) | ||
| 60 | ksym = (ksym - XK_KP_0) + XK_0; | ||
| 61 | else if(IsFunctionKey(ksym) || IsKeypadKey(ksym) | ||
| 62 | || IsMiscFunctionKey(ksym) || IsPFKey(ksym) | ||
| 63 | || IsPrivateKeypadKey(ksym)) | ||
| 64 | return; | ||
| 65 | /* first check if a control mask is omitted */ | ||
| 66 | if(e->state & ControlMask) { | ||
| 67 | switch(tolower(ksym)) { | ||
| 68 | default: | ||
| 69 | return; | ||
| 70 | case XK_a: | ||
| 71 | ksym = XK_Home; | ||
| 72 | break; | ||
| 73 | case XK_b: | ||
| 74 | ksym = XK_Left; | ||
| 75 | break; | ||
| 76 | case XK_c: | ||
| 77 | ksym = XK_Escape; | ||
| 78 | break; | ||
| 79 | case XK_e: | ||
| 80 | ksym = XK_End; | ||
| 81 | break; | ||
| 82 | case XK_f: | ||
| 83 | ksym = XK_Right; | ||
| 84 | break; | ||
| 85 | case XK_h: | ||
| 86 | ksym = XK_BackSpace; | ||
| 87 | break; | ||
| 88 | case XK_j: | ||
| 89 | case XK_m: | ||
| 90 | ksym = XK_Return; | ||
| 91 | break; | ||
| 92 | case XK_k: | ||
| 93 | text[cursor] = '\0'; | ||
| 94 | break; | ||
| 95 | case XK_u: | ||
| 96 | memmove(text, text + cursor, sizeof text - cursor + 1); | ||
| 97 | cursor = 0; | ||
| 98 | break; | ||
| 99 | case XK_w: | ||
| 100 | if(cursor > 0) { | ||
| 101 | i = cursor; | ||
| 102 | while(i-- > 0 && text[i] == ' '); | ||
| 103 | while(i-- > 0 && text[i] != ' '); | ||
| 104 | memmove(text + i + 1, text + cursor, sizeof text - cursor + 1); | ||
| 105 | cursor = i + 1; | ||
| 106 | } | ||
| 107 | break; | ||
| 108 | case XK_y: | ||
| 109 | { | ||
| 110 | FILE *fp; | ||
| 111 | char *s; | ||
| 112 | if(!(fp = popen("sselp", "r"))) | ||
| 113 | eprint("cannot popen sselp\n"); | ||
| 114 | s = fgets(buf, sizeof buf, fp); | ||
| 115 | pclose(fp); | ||
| 116 | if(s == NULL) | ||
| 117 | return; | ||
| 118 | } | ||
| 119 | num = strlen(buf); | ||
| 120 | if(num && buf[num-1] == '\n') | ||
| 121 | buf[--num] = '\0'; | ||
| 122 | break; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | switch(ksym) { | ||
| 126 | default: | ||
| 127 | num = MIN(num, sizeof text - cursor); | ||
| 128 | if(num && !iscntrl((int) buf[0])) { | ||
| 129 | memmove(text + cursor + num, text + cursor, sizeof text - cursor - num); | ||
| 130 | memcpy(text + cursor, buf, num); | ||
| 131 | cursor += num; | ||
| 132 | } | ||
| 133 | break; | ||
| 134 | case XK_BackSpace: | ||
| 135 | if(cursor == 0) | ||
| 136 | return; | ||
| 137 | for(i = 1; cursor - i > 0 && !IS_UTF8_1ST_CHAR(text[cursor - i]); i++); | ||
| 138 | memmove(text + cursor - i, text + cursor, sizeof text - cursor + i); | ||
| 139 | cursor -= i; | ||
| 140 | break; | ||
| 141 | case XK_Delete: | ||
| 142 | if(cursor == len) | ||
| 143 | return; | ||
| 144 | for(i = 1; cursor + i < len && !IS_UTF8_1ST_CHAR(text[cursor + i]); i++); | ||
| 145 | memmove(text + cursor, text + cursor + i, sizeof text - cursor); | ||
| 146 | break; | ||
| 147 | case XK_End: | ||
| 148 | cursor = len; | ||
| 149 | break; | ||
| 150 | case XK_Escape: | ||
| 151 | exit(EXIT_FAILURE); | ||
| 152 | case XK_Home: | ||
| 153 | cursor = 0; | ||
| 154 | break; | ||
| 155 | case XK_Left: | ||
| 156 | if(cursor == 0) | ||
| 157 | return; | ||
| 158 | while(cursor-- > 0 && !IS_UTF8_1ST_CHAR(text[cursor])); | ||
| 159 | break; | ||
| 160 | case XK_Return: | ||
| 161 | fprintf(stdout, "%s", text); | ||
| 162 | fflush(stdout); | ||
| 163 | exit(EXIT_SUCCESS); | ||
| 164 | case XK_Right: | ||
| 165 | if(cursor == len) | ||
| 166 | return; | ||
| 167 | while(cursor++ < len && !IS_UTF8_1ST_CHAR(text[cursor])); | ||
| 168 | break; | ||
| 169 | } | ||
| 170 | drawbar(); | ||
| 171 | } | ||
| 172 | |||
| 173 | int | ||
| 174 | main(int argc, char *argv[]) { | ||
| 175 | unsigned int i; | ||
| 176 | |||
| 177 | /* command line args */ | ||
| 178 | progname = "dinput"; | ||
| 179 | for(i = 1; i < argc; i++) | ||
| 180 | if(!strcmp(argv[i], "-i")) | ||
| 181 | ; /* ignore flag */ | ||
| 182 | else if(!strcmp(argv[i], "-b")) | ||
| 183 | topbar = False; | ||
| 184 | else if(!strcmp(argv[i], "-l")) | ||
| 185 | i++; /* ignore flag */ | ||
| 186 | else if(!strcmp(argv[i], "-fn")) { | ||
| 187 | if(++i < argc) font = argv[i]; | ||
| 188 | } | ||
| 189 | else if(!strcmp(argv[i], "-nb")) { | ||
| 190 | if(++i < argc) normbgcolor = argv[i]; | ||
| 191 | } | ||
| 192 | else if(!strcmp(argv[i], "-nf")) { | ||
| 193 | if(++i < argc) normfgcolor = argv[i]; | ||
| 194 | } | ||
| 195 | else if(!strcmp(argv[i], "-p")) { | ||
| 196 | if(++i < argc) prompt = argv[i]; | ||
| 197 | } | ||
| 198 | else if(!strcmp(argv[i], "-sb")) { | ||
| 199 | if(++i < argc) selbgcolor = argv[i]; | ||
| 200 | } | ||
| 201 | else if(!strcmp(argv[i], "-sf")) { | ||
| 202 | if(++i < argc) selfgcolor = argv[i]; | ||
| 203 | } | ||
| 204 | else if(!strcmp(argv[i], "-v")) { | ||
| 205 | printf("dinput-"VERSION", © 2006-2010 dmenu engineers, see LICENSE for details\n"); | ||
| 206 | exit(EXIT_SUCCESS); | ||
| 207 | } | ||
| 208 | else if(!*text) { | ||
| 209 | strncpy(text, argv[i], sizeof text); | ||
| 210 | cursor = strlen(text); | ||
| 211 | } | ||
| 212 | else { | ||
| 213 | fputs("usage: dinput [-b] [-fn <font>] [-nb <color>] [-nf <color>]\n" | ||
| 214 | " [-p <prompt>] [-sb <color>] [-sf <color>] [-v] [<text>]\n", stderr); | ||
| 215 | exit(EXIT_FAILURE); | ||
| 216 | } | ||
| 217 | if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) | ||
| 218 | fprintf(stderr, "dinput: warning: no locale support\n"); | ||
| 219 | if(!(dpy = XOpenDisplay(NULL))) | ||
| 220 | eprint("cannot open display\n"); | ||
| 221 | if(atexit(&cleanup) != 0) | ||
| 222 | eprint("cannot register cleanup\n"); | ||
| 223 | screen = DefaultScreen(dpy); | ||
| 224 | root = RootWindow(dpy, screen); | ||
| 225 | |||
| 226 | grabkeyboard(); | ||
| 227 | setup(0); | ||
| 228 | run(); | ||
| 229 | return 0; | ||
| 230 | } | ||
| @@ -8,7 +8,16 @@ | |||
| 8 | #include <X11/keysym.h> | 8 | #include <X11/keysym.h> |
| 9 | #include <X11/Xlib.h> | 9 | #include <X11/Xlib.h> |
| 10 | #include <X11/Xutil.h> | 10 | #include <X11/Xutil.h> |
| 11 | #include "dmenu.h" | 11 | #ifdef XINERAMA |
| 12 | #include <X11/extensions/Xinerama.h> | ||
| 13 | #endif | ||
| 14 | #include <draw.h> | ||
| 15 | #include "config.h" | ||
| 16 | |||
| 17 | #define INRECT(x,y,rx,ry,rw,rh) ((rx) < (x) && (x) < (rx)+(rw) && (ry) < (y) && (y) < (ry)+(rh)) | ||
| 18 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) | ||
| 19 | #define MAX(a,b) ((a) > (b) ? (a) : (b)) | ||
| 20 | #define IS_UTF8_1ST_CHAR(c) (((c) & 0xc0) == 0xc0 || ((c) & 0x80) == 0x00) | ||
| 12 | 21 | ||
| 13 | typedef struct Item Item; | 22 | typedef struct Item Item; |
| 14 | struct Item { | 23 | struct Item { |
| @@ -17,30 +26,46 @@ struct Item { | |||
| 17 | Item *left, *right; /* traverses items matching current search pattern */ | 26 | Item *left, *right; /* traverses items matching current search pattern */ |
| 18 | }; | 27 | }; |
| 19 | 28 | ||
| 20 | /* forward declarations */ | ||
| 21 | static void appenditem(Item *i, Item **list, Item **last); | 29 | static void appenditem(Item *i, Item **list, Item **last); |
| 22 | static void calcoffsetsh(void); | 30 | static void calcoffsetsh(void); |
| 23 | static void calcoffsetsv(void); | 31 | static void calcoffsetsv(void); |
| 24 | static char *cistrstr(const char *s, const char *sub); | 32 | static char *cistrstr(const char *s, const char *sub); |
| 25 | static void cleanup(void); | 33 | static void cleanup(void); |
| 26 | static void dinput(void); | ||
| 27 | static void drawitem(const char *s, unsigned long col[ColLast]); | 34 | static void drawitem(const char *s, unsigned long col[ColLast]); |
| 35 | static void drawmenu(void); | ||
| 28 | static void drawmenuh(void); | 36 | static void drawmenuh(void); |
| 29 | static void drawmenuv(void); | 37 | static void drawmenuv(void); |
| 38 | static void grabkeyboard(void); | ||
| 39 | static void keypress(XKeyEvent *e); | ||
| 30 | static void match(void); | 40 | static void match(void); |
| 31 | static void readstdin(void); | 41 | static void readstdin(void); |
| 42 | static void run(void); | ||
| 43 | static void setup(void); | ||
| 32 | 44 | ||
| 33 | /* variables */ | ||
| 34 | static char **argp = NULL; | 45 | static char **argp = NULL; |
| 35 | static char *maxname = NULL; | 46 | static char *maxname = NULL; |
| 47 | static char *prompt; | ||
| 48 | static char text[4096]; | ||
| 49 | static int promptw; | ||
| 50 | static int screen; | ||
| 51 | static size_t cur = 0; | ||
| 36 | static unsigned int cmdw = 0; | 52 | static unsigned int cmdw = 0; |
| 37 | static unsigned int lines = 0; | 53 | static unsigned int lines = 0; |
| 54 | static unsigned int numlockmask; | ||
| 55 | static unsigned int mw, mh; | ||
| 56 | static unsigned long normcol[ColLast]; | ||
| 57 | static unsigned long selcol[ColLast]; | ||
| 58 | static Bool topbar = True; | ||
| 59 | static DC dc; | ||
| 60 | static Display *dpy; | ||
| 38 | static Item *allitems = NULL; /* first of all items */ | 61 | static Item *allitems = NULL; /* first of all items */ |
| 39 | static Item *item = NULL; /* first of pattern matching items */ | 62 | static Item *item = NULL; /* first of pattern matching items */ |
| 40 | static Item *sel = NULL; | 63 | static Item *sel = NULL; |
| 41 | static Item *next = NULL; | 64 | static Item *next = NULL; |
| 42 | static Item *prev = NULL; | 65 | static Item *prev = NULL; |
| 43 | static Item *curr = NULL; | 66 | static Item *curr = NULL; |
| 67 | static Window win, root; | ||
| 68 | |||
| 44 | static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; | 69 | static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; |
| 45 | static char *(*fstrstr)(const char *, const char *) = strstr; | 70 | static char *(*fstrstr)(const char *, const char *) = strstr; |
| 46 | static void (*calcoffsets)(void) = calcoffsetsh; | 71 | static void (*calcoffsets)(void) = calcoffsetsh; |
| @@ -120,16 +145,18 @@ cleanup(void) { | |||
| 120 | } | 145 | } |
| 121 | 146 | ||
| 122 | void | 147 | void |
| 123 | dinput(void) { | 148 | drawitem(const char *s, unsigned long col[ColLast]) { |
| 124 | cleanup(); | 149 | const char *p; |
| 125 | argp[0] = "dinput"; | 150 | unsigned int w = textnw(&dc, text, strlen(text)); |
| 126 | argp[1] = text; | 151 | |
| 127 | execvp("dinput", argp); | 152 | drawbox(&dc, col); |
| 128 | eprint("cannot exec dinput\n"); | 153 | drawtext(&dc, s, col); |
| 154 | for(p = fstrstr(s, text); *text && (p = fstrstr(p, text)); p++) | ||
| 155 | drawline(&dc, textnw(&dc, s, p-s) + dc.h/2 - 1, dc.h-2, w, 1, col); | ||
| 129 | } | 156 | } |
| 130 | 157 | ||
| 131 | void | 158 | void |
| 132 | drawbar(void) { | 159 | drawmenu(void) { |
| 133 | dc.x = 0; | 160 | dc.x = 0; |
| 134 | dc.y = 0; | 161 | dc.y = 0; |
| 135 | dc.w = mw; | 162 | dc.w = mw; |
| @@ -149,6 +176,7 @@ drawbar(void) { | |||
| 149 | if(cmdw && item && lines == 0) | 176 | if(cmdw && item && lines == 0) |
| 150 | dc.w = cmdw; | 177 | dc.w = cmdw; |
| 151 | drawtext(&dc, text, normcol); | 178 | drawtext(&dc, text, normcol); |
| 179 | drawline(&dc, textnw(&dc, text, cur) + dc.h/2 - 2, 2, 1, dc.h-4, normcol); | ||
| 152 | if(lines > 0) | 180 | if(lines > 0) |
| 153 | drawmenuv(); | 181 | drawmenuv(); |
| 154 | else if(curr) | 182 | else if(curr) |
| @@ -157,17 +185,6 @@ drawbar(void) { | |||
| 157 | } | 185 | } |
| 158 | 186 | ||
| 159 | void | 187 | void |
| 160 | drawitem(const char *s, unsigned long col[ColLast]) { | ||
| 161 | const char *p; | ||
| 162 | unsigned int w = textnw(&dc, text, strlen(text)); | ||
| 163 | |||
| 164 | drawbox(&dc, col); | ||
| 165 | drawtext(&dc, s, col); | ||
| 166 | for(p = fstrstr(s, text); *text && (p = fstrstr(p, text)); p++) | ||
| 167 | drawline(&dc, textnw(&dc, s, p-s) + dc.h/2 - 1, dc.h-2, w, 1, col); | ||
| 168 | } | ||
| 169 | |||
| 170 | void | ||
| 171 | drawmenuh(void) { | 188 | drawmenuh(void) { |
| 172 | Item *i; | 189 | Item *i; |
| 173 | 190 | ||
| @@ -202,7 +219,19 @@ drawmenuv(void) { | |||
| 202 | } | 219 | } |
| 203 | 220 | ||
| 204 | void | 221 | void |
| 205 | kpress(XKeyEvent *e) { | 222 | grabkeyboard(void) { |
| 223 | unsigned int n; | ||
| 224 | |||
| 225 | for(n = 0; n < 1000; n++) { | ||
| 226 | if(!XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)) | ||
| 227 | return; | ||
| 228 | usleep(1000); | ||
| 229 | } | ||
| 230 | exit(EXIT_FAILURE); | ||
| 231 | } | ||
| 232 | |||
| 233 | void | ||
| 234 | keypress(XKeyEvent *e) { | ||
| 206 | char buf[sizeof text]; | 235 | char buf[sizeof text]; |
| 207 | int num; | 236 | int num; |
| 208 | unsigned int i, len; | 237 | unsigned int i, len; |
| @@ -248,6 +277,9 @@ kpress(XKeyEvent *e) { | |||
| 248 | case XK_m: | 277 | case XK_m: |
| 249 | ksym = XK_Return; | 278 | ksym = XK_Return; |
| 250 | break; | 279 | break; |
| 280 | case XK_k: | ||
| 281 | text[cur] = '\0'; | ||
| 282 | break; | ||
| 251 | case XK_n: | 283 | case XK_n: |
| 252 | ksym = XK_Down; | 284 | ksym = XK_Down; |
| 253 | break; | 285 | break; |
| @@ -255,38 +287,67 @@ kpress(XKeyEvent *e) { | |||
| 255 | ksym = XK_Up; | 287 | ksym = XK_Up; |
| 256 | break; | 288 | break; |
| 257 | case XK_u: | 289 | case XK_u: |
| 258 | text[0] = '\0'; | 290 | memmove(text, text + cur, sizeof text - cur + 1); |
| 291 | cur = 0; | ||
| 259 | match(); | 292 | match(); |
| 260 | break; | 293 | break; |
| 261 | case XK_w: | 294 | case XK_w: |
| 262 | if(len == 0) | 295 | if(cur == 0) |
| 263 | return; | 296 | return; |
| 264 | i = len; | 297 | i = cur; |
| 265 | while(i-- > 0 && text[i] == ' '); | 298 | while(i-- > 0 && text[i] == ' '); |
| 266 | while(i-- > 0 && text[i] != ' '); | 299 | while(i-- > 0 && text[i] != ' '); |
| 267 | text[++i] = '\0'; | 300 | memmove(text + i + 1, text + cur, sizeof text - cur + 1); |
| 301 | cur = i + 1; | ||
| 268 | match(); | 302 | match(); |
| 269 | break; | 303 | break; |
| 304 | case XK_y: | ||
| 305 | { | ||
| 306 | FILE *fp; | ||
| 307 | char *s; | ||
| 308 | if(!(fp = fopen("sselp", "r"))) | ||
| 309 | eprint("cannot popen sselp\n"); | ||
| 310 | s = fgets(buf, sizeof buf, fp); | ||
| 311 | fclose(fp); | ||
| 312 | if(!s) | ||
| 313 | return; | ||
| 314 | } | ||
| 315 | num = strlen(buf); | ||
| 316 | if(num && buf[num-1] == '\n') | ||
| 317 | buf[--num] = '\0'; | ||
| 318 | break; | ||
| 270 | } | 319 | } |
| 271 | } | 320 | } |
| 272 | switch(ksym) { | 321 | switch(ksym) { |
| 273 | default: | 322 | default: |
| 274 | num = MIN(num, sizeof text); | 323 | num = MIN(num, sizeof text); |
| 275 | if(num && !iscntrl((int) buf[0])) { | 324 | if(num && !iscntrl((int) buf[0])) { |
| 276 | memcpy(text + len, buf, num + 1); | 325 | memmove(text + cur + num, text + cur, sizeof text - cur - num); |
| 277 | len += num; | 326 | memcpy(text + cur, buf, num); |
| 327 | cur += num; | ||
| 278 | match(); | 328 | match(); |
| 279 | } | 329 | } |
| 280 | break; | 330 | break; |
| 281 | case XK_BackSpace: | 331 | case XK_BackSpace: |
| 282 | if(len == 0) | 332 | if(cur == 0) |
| 283 | return; | 333 | return; |
| 284 | for(i = 1; len - i > 0 && !IS_UTF8_1ST_CHAR(text[len - i]); i++); | 334 | for(i = 1; len - i > 0 && !IS_UTF8_1ST_CHAR(text[cur - i]); i++); |
| 285 | len -= i; | 335 | memmove(text + cur - i, text + cur, sizeof text - cur + i); |
| 286 | text[len] = '\0'; | 336 | cur -= i; |
| 337 | match(); | ||
| 338 | break; | ||
| 339 | case XK_Delete: | ||
| 340 | if(cur == len) | ||
| 341 | return; | ||
| 342 | for(i = 1; cur + i < len && !IS_UTF8_1ST_CHAR(text[cur + i]); i++); | ||
| 343 | memmove(text + cur, text + cur + i, sizeof text - cur); | ||
| 287 | match(); | 344 | match(); |
| 288 | break; | 345 | break; |
| 289 | case XK_End: | 346 | case XK_End: |
| 347 | if(cur < len) { | ||
| 348 | cur = len; | ||
| 349 | break; | ||
| 350 | } | ||
| 290 | while(next) { | 351 | while(next) { |
| 291 | sel = curr = next; | 352 | sel = curr = next; |
| 292 | calcoffsets(); | 353 | calcoffsets(); |
| @@ -297,10 +358,20 @@ kpress(XKeyEvent *e) { | |||
| 297 | case XK_Escape: | 358 | case XK_Escape: |
| 298 | exit(EXIT_FAILURE); | 359 | exit(EXIT_FAILURE); |
| 299 | case XK_Home: | 360 | case XK_Home: |
| 361 | if(sel == item) { | ||
| 362 | cur = 0; | ||
| 363 | break; | ||
| 364 | } | ||
| 300 | sel = curr = item; | 365 | sel = curr = item; |
| 301 | calcoffsets(); | 366 | calcoffsets(); |
| 302 | break; | 367 | break; |
| 303 | case XK_Left: | 368 | case XK_Left: |
| 369 | if(cur > 0 && (!sel || !sel->left || lines > 0)) { | ||
| 370 | while(cur-- > 0 && !IS_UTF8_1ST_CHAR(text[cur])); | ||
| 371 | break; | ||
| 372 | } | ||
| 373 | if(lines > 0) | ||
| 374 | return; | ||
| 304 | case XK_Up: | 375 | case XK_Up: |
| 305 | if(!sel || !sel->left) | 376 | if(!sel || !sel->left) |
| 306 | return; | 377 | return; |
| @@ -323,12 +394,16 @@ kpress(XKeyEvent *e) { | |||
| 323 | calcoffsets(); | 394 | calcoffsets(); |
| 324 | break; | 395 | break; |
| 325 | case XK_Return: | 396 | case XK_Return: |
| 326 | if(e->state & ShiftMask) | 397 | fprintf(stdout, "%s", ((e->state & ShiftMask) || sel) ? sel->text : text); |
| 327 | dinput(); | ||
| 328 | fprintf(stdout, "%s", sel ? sel->text : text); | ||
| 329 | fflush(stdout); | 398 | fflush(stdout); |
| 330 | exit(EXIT_SUCCESS); | 399 | exit(EXIT_SUCCESS); |
| 331 | case XK_Right: | 400 | case XK_Right: |
| 401 | if(cur < len) { | ||
| 402 | while(cur++ < len && !IS_UTF8_1ST_CHAR(text[cur])); | ||
| 403 | break; | ||
| 404 | } | ||
| 405 | if(lines > 0) | ||
| 406 | return; | ||
| 332 | case XK_Down: | 407 | case XK_Down: |
| 333 | if(!sel || !sel->right) | 408 | if(!sel || !sel->right) |
| 334 | return; | 409 | return; |
| @@ -339,12 +414,14 @@ kpress(XKeyEvent *e) { | |||
| 339 | } | 414 | } |
| 340 | break; | 415 | break; |
| 341 | case XK_Tab: | 416 | case XK_Tab: |
| 342 | if(sel) | 417 | if(!sel) |
| 343 | strncpy(text, sel->text, sizeof text); | 418 | return; |
| 344 | dinput(); | 419 | strncpy(text, sel->text, sizeof text); |
| 420 | cur = strlen(text); | ||
| 421 | match(); | ||
| 345 | break; | 422 | break; |
| 346 | } | 423 | } |
| 347 | drawbar(); | 424 | drawmenu(); |
| 348 | } | 425 | } |
| 349 | 426 | ||
| 350 | void | 427 | void |
| @@ -413,6 +490,98 @@ readstdin(void) { | |||
| 413 | } | 490 | } |
| 414 | } | 491 | } |
| 415 | 492 | ||
| 493 | void | ||
| 494 | run(void) { | ||
| 495 | XEvent ev; | ||
| 496 | |||
| 497 | XSync(dpy, False); | ||
| 498 | while(!XNextEvent(dpy, &ev)) | ||
| 499 | switch(ev.type) { | ||
| 500 | case Expose: | ||
| 501 | if(ev.xexpose.count == 0) | ||
| 502 | drawmenu(); | ||
| 503 | break; | ||
| 504 | case KeyPress: | ||
| 505 | keypress(&ev.xkey); | ||
| 506 | break; | ||
| 507 | case VisibilityNotify: | ||
| 508 | if(ev.xvisibility.state != VisibilityUnobscured) | ||
| 509 | XRaiseWindow(dpy, win); | ||
| 510 | break; | ||
| 511 | } | ||
| 512 | exit(EXIT_FAILURE); | ||
| 513 | } | ||
| 514 | |||
| 515 | void | ||
| 516 | setup(void) { | ||
| 517 | int i, j, x, y; | ||
| 518 | #if XINERAMA | ||
| 519 | int n; | ||
| 520 | XineramaScreenInfo *info = NULL; | ||
| 521 | #endif | ||
| 522 | XModifierKeymap *modmap; | ||
| 523 | XSetWindowAttributes wa; | ||
| 524 | |||
| 525 | /* init modifier map */ | ||
| 526 | modmap = XGetModifierMapping(dpy); | ||
| 527 | for(i = 0; i < 8; i++) | ||
| 528 | for(j = 0; j < modmap->max_keypermod; j++) { | ||
| 529 | if(modmap->modifiermap[i * modmap->max_keypermod + j] | ||
| 530 | == XKeysymToKeycode(dpy, XK_Num_Lock)) | ||
| 531 | numlockmask = (1 << i); | ||
| 532 | } | ||
| 533 | XFreeModifiermap(modmap); | ||
| 534 | |||
| 535 | dc.dpy = dpy; | ||
| 536 | normcol[ColBG] = getcolor(&dc, normbgcolor); | ||
| 537 | normcol[ColFG] = getcolor(&dc, normfgcolor); | ||
| 538 | selcol[ColBG] = getcolor(&dc, selbgcolor); | ||
| 539 | selcol[ColFG] = getcolor(&dc, selfgcolor); | ||
| 540 | initfont(&dc, font); | ||
| 541 | |||
| 542 | /* input window */ | ||
| 543 | wa.override_redirect = True; | ||
| 544 | wa.background_pixmap = ParentRelative; | ||
| 545 | wa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; | ||
| 546 | |||
| 547 | /* input window geometry */ | ||
| 548 | mh = (dc.font.height + 2) * (lines + 1); | ||
| 549 | #if XINERAMA | ||
| 550 | if(XineramaIsActive(dpy) && (info = XineramaQueryScreens(dpy, &n))) { | ||
| 551 | i = 0; | ||
| 552 | if(n > 1) { | ||
| 553 | int di; | ||
| 554 | unsigned int dui; | ||
| 555 | Window dummy; | ||
| 556 | if(XQueryPointer(dpy, root, &dummy, &dummy, &x, &y, &di, &di, &dui)) | ||
| 557 | for(i = 0; i < n; i++) | ||
| 558 | if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height)) | ||
| 559 | break; | ||
| 560 | } | ||
| 561 | x = info[i].x_org; | ||
| 562 | y = topbar ? info[i].y_org : info[i].y_org + info[i].height - mh; | ||
| 563 | mw = info[i].width; | ||
| 564 | XFree(info); | ||
| 565 | } | ||
| 566 | else | ||
| 567 | #endif | ||
| 568 | { | ||
| 569 | x = 0; | ||
| 570 | y = topbar ? 0 : DisplayHeight(dpy, screen) - mh; | ||
| 571 | mw = DisplayWidth(dpy, screen); | ||
| 572 | } | ||
| 573 | |||
| 574 | win = XCreateWindow(dpy, root, x, y, mw, mh, 0, | ||
| 575 | DefaultDepth(dpy, screen), CopyFromParent, | ||
| 576 | DefaultVisual(dpy, screen), | ||
| 577 | CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); | ||
| 578 | |||
| 579 | setupdraw(&dc, win); | ||
| 580 | if(prompt) | ||
| 581 | promptw = MIN(textw(&dc, prompt), mw / 5); | ||
| 582 | XMapRaised(dpy, win); | ||
| 583 | } | ||
| 584 | |||
| 416 | int | 585 | int |
| 417 | main(int argc, char *argv[]) { | 586 | main(int argc, char *argv[]) { |
| 418 | unsigned int i; | 587 | unsigned int i; |
| @@ -472,7 +641,7 @@ main(int argc, char *argv[]) { | |||
| 472 | 641 | ||
| 473 | readstdin(); | 642 | readstdin(); |
| 474 | grabkeyboard(); | 643 | grabkeyboard(); |
| 475 | setup(lines); | 644 | setup(); |
| 476 | if(maxname) | 645 | if(maxname) |
| 477 | cmdw = MIN(textw(&dc, maxname), mw / 3); | 646 | cmdw = MIN(textw(&dc, maxname), mw / 3); |
| 478 | match(); | 647 | match(); |
diff --git a/dmenu.h b/dmenu.h deleted file mode 100644 index 4cc13f4..0000000 --- a/dmenu.h +++ /dev/null | |||
| @@ -1,30 +0,0 @@ | |||
| 1 | #include <X11/Xlib.h> | ||
| 2 | #include <draw.h> | ||
| 3 | #include "config.h" | ||
| 4 | |||
| 5 | /* macros */ | ||
| 6 | #define INRECT(X,Y,RX,RY,RW,RH) ((X) >= (RX) && (X) < (RX) + (RW) && (Y) >= (RY) && (Y) < (RY) + (RH)) | ||
| 7 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) | ||
| 8 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) | ||
| 9 | #define IS_UTF8_1ST_CHAR(c) ((((c) & 0xc0) == 0xc0) || !((c) & 0x80)) | ||
| 10 | |||
| 11 | /* forward declarations */ | ||
| 12 | void drawbar(void); | ||
| 13 | void grabkeyboard(void); | ||
| 14 | void kpress(XKeyEvent *e); | ||
| 15 | void run(void); | ||
| 16 | void setup(unsigned int lines); | ||
| 17 | |||
| 18 | /* variables */ | ||
| 19 | extern char *prompt; | ||
| 20 | extern char text[4096]; | ||
| 21 | extern int promptw; | ||
| 22 | extern int screen; | ||
| 23 | extern unsigned int numlockmask; | ||
| 24 | extern unsigned int mw, mh; | ||
| 25 | extern unsigned long normcol[ColLast]; | ||
| 26 | extern unsigned long selcol[ColLast]; | ||
| 27 | extern Bool topbar; | ||
| 28 | extern DC dc; | ||
| 29 | extern Display *dpy; | ||
| 30 | extern Window win, root; | ||
