diff options
| author | Connor Lane Smith <cls@lubutu.com> | 2010-07-31 14:56:27 +0100 |
|---|---|---|
| committer | Connor Lane Smith <cls@lubutu.com> | 2010-07-31 14:56:27 +0100 |
| commit | a3606ecb0eeaffa752fdf26565ebaaba29b83239 (patch) | |
| tree | a00a059252baff25ef5837605749555efd653c5b | |
| parent | 7d5fe17391fefd978eab281a6cbebe2aa601e9ce (diff) | |
updated manpage, added paste, cleaned up, new libdraw
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | README | 6 | ||||
| -rw-r--r-- | dmenu.1 | 94 | ||||
| -rw-r--r-- | dmenu.c | 488 |
4 files changed, 271 insertions, 319 deletions
| @@ -6,7 +6,7 @@ include config.mk | |||
| 6 | SRC = dmenu.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 dmenu |
| 10 | 10 | ||
| 11 | options: | 11 | options: |
| 12 | @echo dmenu build options: | 12 | @echo dmenu build options: |
| @@ -8,13 +8,11 @@ Requirements | |||
| 8 | In order to build dmenu you need the Xlib header files. | 8 | In order to build dmenu you need the Xlib header files. |
| 9 | You also need libdraw, available from http://hg.suckless.org/libdraw | 9 | You also need libdraw, available from http://hg.suckless.org/libdraw |
| 10 | 10 | ||
| 11 | Pasting the X selection to dmenu requires the sselp utility at runtime. | ||
| 12 | |||
| 13 | 11 | ||
| 14 | Installation | 12 | Installation |
| 15 | ------------ | 13 | ------------ |
| 16 | Edit config.mk to match your local setup (dmenu is installed into | 14 | Edit config.mk to match your local setup (dmenu is installed into the |
| 17 | the /usr/local namespace by default). | 15 | /usr/local namespace by default). |
| 18 | 16 | ||
| 19 | Afterwards enter the following command to build and install dmenu (if | 17 | Afterwards enter the following command to build and install dmenu (if |
| 20 | necessary as root): | 18 | necessary as root): |
| @@ -3,99 +3,103 @@ | |||
| 3 | dmenu \- dynamic menu | 3 | dmenu \- dynamic menu |
| 4 | .SH SYNOPSIS | 4 | .SH SYNOPSIS |
| 5 | .B dmenu | 5 | .B dmenu |
| 6 | .RB [ \-i ] | ||
| 7 | .RB [ \-b ] | 6 | .RB [ \-b ] |
| 8 | .RB [ \-e " <xid>]" | 7 | .RB [ \-i ] |
| 9 | .RB [ \-l " <lines>]" | 8 | .RB [ \-l " <lines>]" |
| 9 | .RB [ \-p " <prompt>]" | ||
| 10 | .RB [ \-fn " <font>]" | 10 | .RB [ \-fn " <font>]" |
| 11 | .RB [ \-nb " <color>]" | 11 | .RB [ \-nb " <color>]" |
| 12 | .RB [ \-nf " <color>]" | 12 | .RB [ \-nf " <color>]" |
| 13 | .RB [ \-p " <prompt>]" | ||
| 14 | .RB [ \-sb " <color>]" | 13 | .RB [ \-sb " <color>]" |
| 15 | .RB [ \-sf " <color>]" | 14 | .RB [ \-sf " <color>]" |
| 16 | .RB [ \-v ] | 15 | .RB [ \-v ] |
| 17 | 16 | ||
| 18 | .B dmenu_run | 17 | .B dmenu_run |
| 19 | [<options...>] | 18 | .RB [ \-b ] |
| 19 | .RB [ \-i ] | ||
| 20 | .RB [ \-l " <lines>]" | ||
| 21 | .RB [ \-p " <prompt>]" | ||
| 22 | .RB [ \-fn " <font>]" | ||
| 23 | .RB [ \-nb " <color>]" | ||
| 24 | .RB [ \-nf " <color>]" | ||
| 25 | .RB [ \-sb " <color>]" | ||
| 26 | .RB [ \-sf " <color>]" | ||
| 27 | .RB [ \-v ] | ||
| 20 | 28 | ||
| 21 | .B dmenu_path | 29 | .B dmenu_path |
| 22 | .SH DESCRIPTION | 30 | .SH DESCRIPTION |
| 23 | .SS Overview | 31 | .SS Overview |
| 24 | dmenu is a generic menu for X, originally designed for | 32 | .B dmenu |
| 33 | is a generic menu for X, originally designed for | ||
| 25 | .BR dwm (1). | 34 | .BR dwm (1). |
| 26 | It manages huge amounts (up to 10.000 and more) of user defined menu items | 35 | It manages huge amounts (10000 and more) of user defined menu items efficiently. |
| 27 | efficiently. | 36 | .P |
| 28 | 37 | .B dmenu_run | |
| 29 | dmenu_run is a dmenu script used by dwm which lists executables in the user's PATH | 38 | is a dmenu script which lists programs in the user's PATH and executes |
| 30 | and executes the selected item. | 39 | the selected item. |
| 31 | 40 | .P | |
| 32 | dmenu_path is a script used by dmenu_run to find and cache a list of executables. | 41 | .B dmenu_path |
| 42 | is a script used by | ||
| 43 | .I dmenu_run | ||
| 44 | to find and cache a list of programs. | ||
| 33 | .SS Options | 45 | .SS Options |
| 34 | .TP | 46 | .TP |
| 35 | .B \-i | ||
| 36 | makes dmenu match menu entries case insensitively. | ||
| 37 | .TP | ||
| 38 | .B \-b | 47 | .B \-b |
| 39 | defines that dmenu appears at the bottom. | 48 | dmenu appears at the bottom of the screen. |
| 40 | .TP | 49 | .TP |
| 41 | .B \-e <xid> | 50 | .B \-i |
| 42 | reparents dmenu to the window specified by xid. | 51 | dmenu matches menu entries case insensitively. |
| 43 | .TP | 52 | .TP |
| 44 | .B \-l <lines> | 53 | .B \-l <lines> |
| 45 | activates vertical list mode. | 54 | dmenu lists items vertically, with the given number of lines. |
| 46 | The given number of lines will be displayed. Window height will be adjusted. | 55 | .TP |
| 56 | .B \-p <prompt> | ||
| 57 | sets the prompt to be displayed to the left of the input area. | ||
| 47 | .TP | 58 | .TP |
| 48 | .B \-fn <font> | 59 | .B \-fn <font> |
| 49 | defines the font. | 60 | sets the font. |
| 50 | .TP | 61 | .TP |
| 51 | .B \-nb <color> | 62 | .B \-nb <color> |
| 52 | defines the normal background color (#RGB, #RRGGBB, and color names are supported). | 63 | sets the background color (#RGB, #RRGGBB, and color names are supported). |
| 53 | .TP | 64 | .TP |
| 54 | .B \-nf <color> | 65 | .B \-nf <color> |
| 55 | defines the normal foreground color (#RGB, #RRGGBB, and color names are supported). | 66 | sets the foreground color (#RGB, #RRGGBB, and color names are supported). |
| 56 | .TP | ||
| 57 | .B \-p <prompt> | ||
| 58 | defines a prompt to be displayed before the input area. | ||
| 59 | .TP | 67 | .TP |
| 60 | .B \-sb <color> | 68 | .B \-sb <color> |
| 61 | defines the selected background color (#RGB, #RRGGBB, and color names are supported). | 69 | sets the background color of selected items (#RGB, #RRGGBB, and color names are |
| 70 | supported). | ||
| 62 | .TP | 71 | .TP |
| 63 | .B \-sf <color> | 72 | .B \-sf <color> |
| 64 | defines the selected foreground color (#RGB, #RRGGBB, and color names are supported). | 73 | sets the foreground color of selected items (#RGB, #RRGGBB, and color names are |
| 74 | supported). | ||
| 65 | .TP | 75 | .TP |
| 66 | .B \-v | 76 | .B \-v |
| 67 | prints version information to standard output, then exits. | 77 | prints version information to standard output, then exits. |
| 68 | .SH USAGE | 78 | .SH USAGE |
| 69 | dmenu reads a list of newline-separated items from standard input and creates a | 79 | dmenu reads a list of newline-separated items from standard input and creates a |
| 70 | menu. When the user selects an item or enters any text and presses Return, his/her | 80 | menu. When the user selects an item or enters any text and presses Return, |
| 71 | choice is printed to standard output and dmenu terminates. | 81 | their choice is printed to standard output and dmenu terminates. |
| 72 | .P | 82 | .P |
| 73 | dmenu is completely controlled by the keyboard. Besides standard Unix line editing, | 83 | dmenu is completely controlled by the keyboard. Besides standard Unix line |
| 74 | and item selection (Up/Down or Left/Right, PageUp/PageDown, Home/End), the following | 84 | editing and item selection (Up/Down/Left/Right, PageUp/PageDown, Home/End), the |
| 75 | keys are recognized: | 85 | following keys are recognized: |
| 76 | .TP | 86 | .TP |
| 77 | .B Tab (Control\-i) | 87 | .B Tab (Control\-i) |
| 78 | Copy the selected item to the input field. | 88 | Copy the selected item to the input field. |
| 79 | .TP | 89 | .TP |
| 80 | .B Return (Control\-j) | 90 | .B Return (Control\-j) |
| 81 | Confirm selection and quit (print the selected item to standard output). Returns | 91 | Confirm selection. Prints the selected item to standard output and exits, |
| 82 | .B 0 | 92 | returning success. |
| 83 | on termination. | ||
| 84 | .TP | 93 | .TP |
| 85 | .B Shift\-Return (Control\-Shift\-j) | 94 | .B Shift\-Return (Control\-Shift\-j) |
| 86 | Confirm input and quit (print the text in the input field to standard output). | 95 | Confirm input. Prints the input text to standard output and exits, returning |
| 87 | Returns | 96 | success. |
| 88 | .B 0 | ||
| 89 | on termination. | ||
| 90 | .TP | 97 | .TP |
| 91 | .B Escape (Control\-c) | 98 | .B Escape (Control\-c) |
| 92 | Quit without selecting an item. Returns | 99 | Quit without selecting an item, returning failure. |
| 93 | .B 1 | ||
| 94 | on termination. | ||
| 95 | .TP | 100 | .TP |
| 96 | .B Control\-y | 101 | .B Control\-y |
| 97 | Pastes the X selection into the input field. This requires | 102 | Paste the current X selection into the input field. |
| 98 | .BR sselp (1). | ||
| 99 | .SH SEE ALSO | 103 | .SH SEE ALSO |
| 100 | .BR dwm (1), | 104 | .BR dwm (1), |
| 101 | .BR wmii (1). | 105 | .BR wmii (1). |
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <string.h> | 6 | #include <string.h> |
| 7 | #include <unistd.h> | 7 | #include <unistd.h> |
| 8 | #include <X11/keysym.h> | 8 | #include <X11/keysym.h> |
| 9 | #include <X11/Xatom.h> | ||
| 9 | #include <X11/Xlib.h> | 10 | #include <X11/Xlib.h> |
| 10 | #include <X11/Xutil.h> | 11 | #include <X11/Xutil.h> |
| 11 | #ifdef XINERAMA | 12 | #ifdef XINERAMA |
| @@ -14,78 +15,72 @@ | |||
| 14 | #include <draw.h> | 15 | #include <draw.h> |
| 15 | #include "config.h" | 16 | #include "config.h" |
| 16 | 17 | ||
| 17 | #define INRECT(x,y,rx,ry,rw,rh) ((rx) < (x) && (x) < (rx)+(rw) && (ry) < (y) && (y) < (ry)+(rh)) | 18 | #define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh)) |
| 18 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) | 19 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) |
| 19 | #define MAX(a,b) ((a) > (b) ? (a) : (b)) | 20 | #define MAX(a,b) ((a) > (b) ? (a) : (b)) |
| 20 | #define IS_UTF8_1ST_CHAR(c) (((c) & 0xc0) == 0xc0 || ((c) & 0x80) == 0x00) | 21 | #define UTF8_CODEPOINT(c) (((c) & 0xc0) != 0x80) |
| 21 | 22 | ||
| 22 | typedef struct Item Item; | 23 | typedef struct Item Item; |
| 23 | struct Item { | 24 | struct Item { |
| 24 | char *text; | 25 | char *text; |
| 25 | Item *next; /* traverses all items */ | 26 | Item *next; /* traverses all items */ |
| 26 | Item *left, *right; /* traverses items matching current search pattern */ | 27 | Item *left, *right; /* traverses matching items */ |
| 27 | }; | 28 | }; |
| 28 | 29 | ||
| 29 | static void appenditem(Item *i, Item **list, Item **last); | 30 | static void appenditem(Item *item, Item **list, Item **last); |
| 30 | static void calcoffsetsh(void); | 31 | static void calcoffsetsh(void); |
| 31 | static void calcoffsetsv(void); | 32 | static void calcoffsetsv(void); |
| 32 | static char *cistrstr(const char *s, const char *sub); | 33 | static char *cistrstr(const char *s, const char *sub); |
| 33 | static void cleanup(void); | ||
| 34 | static void drawitem(const char *s, unsigned long col[ColLast]); | ||
| 35 | static void drawmenu(void); | 34 | static void drawmenu(void); |
| 36 | static void drawmenuh(void); | 35 | static void drawmenuh(void); |
| 37 | static void drawmenuv(void); | 36 | static void drawmenuv(void); |
| 38 | static void grabkeyboard(void); | 37 | static void grabkeyboard(void); |
| 38 | static void insert(const char *s, ssize_t n); | ||
| 39 | static void keypress(XKeyEvent *e); | 39 | static void keypress(XKeyEvent *e); |
| 40 | static void match(void); | 40 | static void match(void); |
| 41 | static void paste(Atom atom); | ||
| 41 | static void readstdin(void); | 42 | static void readstdin(void); |
| 42 | static void run(void); | 43 | static void run(void); |
| 43 | static void setup(void); | 44 | static void setup(void); |
| 45 | static void usage(void); | ||
| 44 | 46 | ||
| 45 | static char **argp = NULL; | ||
| 46 | static char *maxname = NULL; | ||
| 47 | static char *prompt; | 47 | static char *prompt; |
| 48 | static char text[4096]; | 48 | static char text[4096]; |
| 49 | static int promptw; | ||
| 50 | static int screen; | 49 | static int screen; |
| 51 | static size_t cur = 0; | 50 | static size_t cursor = 0; |
| 52 | static unsigned int cmdw = 0; | 51 | static unsigned int inputw = 0; |
| 53 | static unsigned int lines = 0; | 52 | static unsigned int lines = 0; |
| 54 | static unsigned int numlockmask; | ||
| 55 | static unsigned int mw, mh; | 53 | static unsigned int mw, mh; |
| 54 | static unsigned int promptw = 0; | ||
| 56 | static unsigned long normcol[ColLast]; | 55 | static unsigned long normcol[ColLast]; |
| 57 | static unsigned long selcol[ColLast]; | 56 | static unsigned long selcol[ColLast]; |
| 57 | static Atom utf8; | ||
| 58 | static Bool topbar = True; | 58 | static Bool topbar = True; |
| 59 | static DC dc; | 59 | static DC dc; |
| 60 | static Display *dpy; | 60 | static Item *allitems, *matches; |
| 61 | static Item *allitems = NULL; /* first of all items */ | 61 | static Item *curr, *prev, *next, *sel; |
| 62 | static Item *item = NULL; /* first of pattern matching items */ | 62 | static Window root, win; |
| 63 | static Item *sel = NULL; | ||
| 64 | static Item *next = NULL; | ||
| 65 | static Item *prev = NULL; | ||
| 66 | static Item *curr = NULL; | ||
| 67 | static Window win, root; | ||
| 68 | 63 | ||
| 69 | static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; | 64 | static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; |
| 70 | static char *(*fstrstr)(const char *, const char *) = strstr; | 65 | static char *(*fstrstr)(const char *, const char *) = strstr; |
| 71 | static void (*calcoffsets)(void) = calcoffsetsh; | 66 | static void (*calcoffsets)(void) = calcoffsetsh; |
| 72 | 67 | ||
| 73 | void | 68 | void |
| 74 | appenditem(Item *i, Item **list, Item **last) { | 69 | appenditem(Item *item, Item **list, Item **last) { |
| 75 | if(!(*last)) | 70 | if(!(*last)) |
| 76 | *list = i; | 71 | *list = item; |
| 77 | else | 72 | else |
| 78 | (*last)->right = i; | 73 | (*last)->right = item; |
| 79 | i->left = *last; | 74 | item->left = *last; |
| 80 | i->right = NULL; | 75 | item->right = NULL; |
| 81 | *last = i; | 76 | *last = item; |
| 82 | } | 77 | } |
| 83 | 78 | ||
| 84 | void | 79 | void |
| 85 | calcoffsetsh(void) { | 80 | calcoffsetsh(void) { |
| 86 | unsigned int w, x; | 81 | unsigned int w, x; |
| 87 | 82 | ||
| 88 | w = promptw + cmdw + textw(&dc, "<") + textw(&dc, ">"); | 83 | w = promptw + inputw + textw(&dc, "<") + textw(&dc, ">"); |
| 89 | for(x = w, next = curr; next; next = next->right) | 84 | for(x = w, next = curr; next; next = next->right) |
| 90 | if((x += MIN(textw(&dc, next->text), mw / 3)) > mw) | 85 | if((x += MIN(textw(&dc, next->text), mw / 3)) > mw) |
| 91 | break; | 86 | break; |
| @@ -129,33 +124,6 @@ cistrstr(const char *s, const char *sub) { | |||
| 129 | } | 124 | } |
| 130 | 125 | ||
| 131 | void | 126 | void |
| 132 | cleanup(void) { | ||
| 133 | Item *itm; | ||
| 134 | |||
| 135 | while(allitems) { | ||
| 136 | itm = allitems->next; | ||
| 137 | free(allitems->text); | ||
| 138 | free(allitems); | ||
| 139 | allitems = itm; | ||
| 140 | } | ||
| 141 | cleanupdraw(&dc); | ||
| 142 | XDestroyWindow(dpy, win); | ||
| 143 | XUngrabKeyboard(dpy, CurrentTime); | ||
| 144 | XCloseDisplay(dpy); | ||
| 145 | } | ||
| 146 | |||
| 147 | void | ||
| 148 | drawitem(const char *s, unsigned long col[ColLast]) { | ||
| 149 | const char *p; | ||
| 150 | unsigned int w = textnw(&dc, text, strlen(text)); | ||
| 151 | |||
| 152 | drawbox(&dc, col); | ||
| 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); | ||
| 156 | } | ||
| 157 | |||
| 158 | void | ||
| 159 | drawmenu(void) { | 127 | drawmenu(void) { |
| 160 | dc.x = 0; | 128 | dc.x = 0; |
| 161 | dc.y = 0; | 129 | dc.y = 0; |
| @@ -172,82 +140,89 @@ drawmenu(void) { | |||
| 172 | dc.x += dc.w; | 140 | dc.x += dc.w; |
| 173 | } | 141 | } |
| 174 | dc.w = mw - dc.x; | 142 | dc.w = mw - dc.x; |
| 175 | /* print command */ | 143 | /* print input area */ |
| 176 | if(cmdw && item && lines == 0) | 144 | if(matches && lines == 0 && textw(&dc, text) <= inputw) |
| 177 | dc.w = cmdw; | 145 | dc.w = inputw; |
| 178 | drawtext(&dc, text, normcol); | 146 | drawtext(&dc, text, normcol); |
| 179 | drawline(&dc, textnw(&dc, text, cur) + dc.h/2 - 2, 2, 1, dc.h-4, normcol); | 147 | drawline(&dc, textnw(&dc, text, cursor) + dc.h/2 - 2, 2, 1, dc.h-4, normcol); |
| 180 | if(lines > 0) | 148 | if(lines > 0) |
| 181 | drawmenuv(); | 149 | drawmenuv(); |
| 182 | else if(curr) | 150 | else if(curr && (dc.w == inputw || curr->next)) |
| 183 | drawmenuh(); | 151 | drawmenuh(); |
| 184 | commitdraw(&dc, win); | 152 | commitdraw(&dc, win); |
| 185 | } | 153 | } |
| 186 | 154 | ||
| 187 | void | 155 | void |
| 188 | drawmenuh(void) { | 156 | drawmenuh(void) { |
| 189 | Item *i; | 157 | Item *item; |
| 190 | 158 | ||
| 191 | dc.x += cmdw; | 159 | dc.x += inputw; |
| 192 | dc.w = textw(&dc, "<"); | 160 | dc.w = textw(&dc, "<"); |
| 193 | drawtext(&dc, curr->left ? "<" : NULL, normcol); | 161 | if(curr->left) |
| 162 | drawtext(&dc, "<", normcol); | ||
| 194 | dc.x += dc.w; | 163 | dc.x += dc.w; |
| 195 | for(i = curr; i != next; i = i->right) { | 164 | for(item = curr; item != next; item = item->right) { |
| 196 | dc.w = MIN(textw(&dc, i->text), mw / 3); | 165 | dc.w = MIN(textw(&dc, item->text), mw / 3); |
| 197 | drawitem(i->text, (sel == i) ? selcol : normcol); | 166 | if(item == sel) |
| 167 | drawbox(&dc, selcol); | ||
| 168 | drawtext(&dc, item->text, (item == sel) ? selcol : normcol); | ||
| 198 | dc.x += dc.w; | 169 | dc.x += dc.w; |
| 199 | } | 170 | } |
| 200 | dc.w = textw(&dc, ">"); | 171 | dc.w = textw(&dc, ">"); |
| 201 | dc.x = mw - dc.w; | 172 | dc.x = mw - dc.w; |
| 202 | drawtext(&dc, next ? ">" : NULL, normcol); | 173 | if(next) |
| 174 | drawtext(&dc, ">", normcol); | ||
| 203 | } | 175 | } |
| 204 | 176 | ||
| 205 | void | 177 | void |
| 206 | drawmenuv(void) { | 178 | drawmenuv(void) { |
| 207 | Item *i; | 179 | Item *item; |
| 208 | XWindowAttributes wa; | 180 | XWindowAttributes wa; |
| 209 | 181 | ||
| 210 | dc.y = topbar ? dc.h : 0; | 182 | dc.y = topbar ? dc.h : 0; |
| 211 | dc.w = mw - dc.x; | 183 | dc.w = mw - dc.x; |
| 212 | for(i = curr; i != next; i = i->right) { | 184 | for(item = curr; item != next; item = item->right) { |
| 213 | drawitem(i->text, (sel == i) ? selcol : normcol); | 185 | if(item == sel) |
| 186 | drawbox(&dc, selcol); | ||
| 187 | drawtext(&dc, item->text, (item == sel) ? selcol : normcol); | ||
| 214 | dc.y += dc.h; | 188 | dc.y += dc.h; |
| 215 | } | 189 | } |
| 216 | if(!XGetWindowAttributes(dpy, win, &wa)) | 190 | if(!XGetWindowAttributes(dc.dpy, win, &wa)) |
| 217 | eprint("cannot get window attributes"); | 191 | eprintf("cannot get window attributes\n"); |
| 218 | XMoveResizeWindow(dpy, win, wa.x, wa.y + (topbar ? 0 : wa.height - mh), mw, mh); | 192 | if(wa.height != mh) |
| 193 | XMoveResizeWindow(dc.dpy, win, wa.x, wa.y + (topbar ? 0 : wa.height - mh), mw, mh); | ||
| 219 | } | 194 | } |
| 220 | 195 | ||
| 221 | void | 196 | void |
| 222 | grabkeyboard(void) { | 197 | grabkeyboard(void) { |
| 223 | unsigned int n; | 198 | int i; |
| 224 | 199 | ||
| 225 | for(n = 0; n < 1000; n++) { | 200 | for(i = 0; i < 1000; i++) { |
| 226 | if(!XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)) | 201 | if(!XGrabKeyboard(dc.dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)) |
| 227 | return; | 202 | return; |
| 228 | usleep(1000); | 203 | usleep(1000); |
| 229 | } | 204 | } |
| 230 | exit(EXIT_FAILURE); | 205 | eprintf("cannot grab keyboard\n"); |
| 206 | } | ||
| 207 | |||
| 208 | void | ||
| 209 | insert(const char *s, ssize_t n) { | ||
| 210 | memmove(text + cursor + n, text + cursor, sizeof text - cursor - n); | ||
| 211 | if(n > 0) | ||
| 212 | memcpy(text + cursor, s, n); | ||
| 213 | cursor += n; | ||
| 214 | match(); | ||
| 231 | } | 215 | } |
| 232 | 216 | ||
| 233 | void | 217 | void |
| 234 | keypress(XKeyEvent *e) { | 218 | keypress(XKeyEvent *e) { |
| 235 | char buf[sizeof text]; | 219 | char buf[sizeof text]; |
| 236 | int num; | 220 | int n; |
| 237 | unsigned int i, len; | 221 | size_t len; |
| 238 | KeySym ksym; | 222 | KeySym ksym; |
| 239 | 223 | ||
| 240 | len = strlen(text); | 224 | len = strlen(text); |
| 241 | num = XLookupString(e, buf, sizeof buf, &ksym, NULL); | 225 | XLookupString(e, buf, sizeof buf, &ksym, NULL); |
| 242 | if(ksym == XK_KP_Enter) | ||
| 243 | ksym = XK_Return; | ||
| 244 | else if(ksym >= XK_KP_0 && ksym <= XK_KP_9) | ||
| 245 | ksym = (ksym - XK_KP_0) + XK_0; | ||
| 246 | else if(IsFunctionKey(ksym) || IsKeypadKey(ksym) | ||
| 247 | || IsMiscFunctionKey(ksym) || IsPFKey(ksym) | ||
| 248 | || IsPrivateKeypadKey(ksym)) | ||
| 249 | return; | ||
| 250 | /* first check if a control mask is omitted */ | ||
| 251 | if(e->state & ControlMask) { | 226 | if(e->state & ControlMask) { |
| 252 | switch(tolower(ksym)) { | 227 | switch(tolower(ksym)) { |
| 253 | default: | 228 | default: |
| @@ -277,8 +252,8 @@ keypress(XKeyEvent *e) { | |||
| 277 | case XK_m: | 252 | case XK_m: |
| 278 | ksym = XK_Return; | 253 | ksym = XK_Return; |
| 279 | break; | 254 | break; |
| 280 | case XK_k: | 255 | case XK_k: /* delete right */ |
| 281 | text[cur] = '\0'; | 256 | text[cursor] = '\0'; |
| 282 | break; | 257 | break; |
| 283 | case XK_n: | 258 | case XK_n: |
| 284 | ksym = XK_Down; | 259 | ksym = XK_Down; |
| @@ -286,66 +261,44 @@ keypress(XKeyEvent *e) { | |||
| 286 | case XK_p: | 261 | case XK_p: |
| 287 | ksym = XK_Up; | 262 | ksym = XK_Up; |
| 288 | break; | 263 | break; |
| 289 | case XK_u: | 264 | case XK_u: /* delete left */ |
| 290 | memmove(text, text + cur, sizeof text - cur + 1); | 265 | insert(NULL, -cursor); |
| 291 | cur = 0; | ||
| 292 | match(); | ||
| 293 | break; | 266 | break; |
| 294 | case XK_w: | 267 | case XK_w: /* delete word */ |
| 295 | if(cur == 0) | 268 | if(cursor == 0) |
| 296 | return; | 269 | return; |
| 297 | i = cur; | 270 | n = 0; |
| 298 | while(i-- > 0 && text[i] == ' '); | 271 | while(cursor - n++ > 0 && text[cursor - n] == ' '); |
| 299 | while(i-- > 0 && text[i] != ' '); | 272 | while(cursor - n++ > 0 && text[cursor - n] != ' '); |
| 300 | memmove(text + i + 1, text + cur, sizeof text - cur + 1); | 273 | insert(NULL, -(--n)); |
| 301 | cur = i + 1; | ||
| 302 | match(); | ||
| 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; | 274 | break; |
| 275 | case XK_y: /* paste selection */ | ||
| 276 | XConvertSelection(dc.dpy, XA_PRIMARY, utf8, None, win, CurrentTime); | ||
| 277 | /* causes SelectionNotify event */ | ||
| 278 | return; | ||
| 319 | } | 279 | } |
| 320 | } | 280 | } |
| 321 | switch(ksym) { | 281 | switch(ksym) { |
| 322 | default: | 282 | default: |
| 323 | num = MIN(num, sizeof text); | 283 | if(!iscntrl((int)*buf)) |
| 324 | if(num && !iscntrl((int) buf[0])) { | 284 | insert(buf, MIN(strlen(buf), sizeof text - cursor)); |
| 325 | memmove(text + cur + num, text + cur, sizeof text - cur - num); | ||
| 326 | memcpy(text + cur, buf, num); | ||
| 327 | cur += num; | ||
| 328 | match(); | ||
| 329 | } | ||
| 330 | break; | 285 | break; |
| 331 | case XK_BackSpace: | 286 | case XK_BackSpace: |
| 332 | if(cur == 0) | 287 | if(cursor == 0) |
| 333 | return; | 288 | return; |
| 334 | for(i = 1; len - i > 0 && !IS_UTF8_1ST_CHAR(text[cur - i]); i++); | 289 | for(n = 1; cursor - n > 0 && !UTF8_CODEPOINT(text[cursor - n]); n++); |
| 335 | memmove(text + cur - i, text + cur, sizeof text - cur + i); | 290 | insert(NULL, -n); |
| 336 | cur -= i; | ||
| 337 | match(); | ||
| 338 | break; | 291 | break; |
| 339 | case XK_Delete: | 292 | case XK_Delete: |
| 340 | if(cur == len) | 293 | if(cursor == len) |
| 341 | return; | 294 | return; |
| 342 | for(i = 1; cur + i < len && !IS_UTF8_1ST_CHAR(text[cur + i]); i++); | 295 | for(n = 1; cursor + n < len && !UTF8_CODEPOINT(text[cursor + n]); n++); |
| 343 | memmove(text + cur, text + cur + i, sizeof text - cur); | 296 | cursor += n; |
| 344 | match(); | 297 | insert(NULL, -n); |
| 345 | break; | 298 | break; |
| 346 | case XK_End: | 299 | case XK_End: |
| 347 | if(cur < len) { | 300 | if(cursor < len) { |
| 348 | cur = len; | 301 | cursor = len; |
| 349 | break; | 302 | break; |
| 350 | } | 303 | } |
| 351 | while(next) { | 304 | while(next) { |
| @@ -358,19 +311,19 @@ keypress(XKeyEvent *e) { | |||
| 358 | case XK_Escape: | 311 | case XK_Escape: |
| 359 | exit(EXIT_FAILURE); | 312 | exit(EXIT_FAILURE); |
| 360 | case XK_Home: | 313 | case XK_Home: |
| 361 | if(sel == item) { | 314 | if(sel == matches) { |
| 362 | cur = 0; | 315 | cursor = 0; |
| 363 | break; | 316 | break; |
| 364 | } | 317 | } |
| 365 | sel = curr = item; | 318 | sel = curr = matches; |
| 366 | calcoffsets(); | 319 | calcoffsets(); |
| 367 | break; | 320 | break; |
| 368 | case XK_Left: | 321 | case XK_Left: |
| 369 | if(cur > 0 && (!sel || !sel->left || lines > 0)) { | 322 | if(cursor > 0 && (!sel || !sel->left || lines > 0)) { |
| 370 | while(cur-- > 0 && !IS_UTF8_1ST_CHAR(text[cur])); | 323 | while(cursor-- > 0 && !UTF8_CODEPOINT(text[cursor])); |
| 371 | break; | 324 | break; |
| 372 | } | 325 | } |
| 373 | if(lines > 0) | 326 | else if(lines > 0) |
| 374 | return; | 327 | return; |
| 375 | case XK_Up: | 328 | case XK_Up: |
| 376 | if(!sel || !sel->left) | 329 | if(!sel || !sel->left) |
| @@ -394,15 +347,16 @@ keypress(XKeyEvent *e) { | |||
| 394 | calcoffsets(); | 347 | calcoffsets(); |
| 395 | break; | 348 | break; |
| 396 | case XK_Return: | 349 | case XK_Return: |
| 397 | fprintf(stdout, "%s", ((e->state & ShiftMask) || sel) ? sel->text : text); | 350 | case XK_KP_Enter: |
| 351 | fputs(((e->state & ShiftMask) || sel) ? sel->text : text, stdout); | ||
| 398 | fflush(stdout); | 352 | fflush(stdout); |
| 399 | exit(EXIT_SUCCESS); | 353 | exit(EXIT_SUCCESS); |
| 400 | case XK_Right: | 354 | case XK_Right: |
| 401 | if(cur < len) { | 355 | if(cursor < len) { |
| 402 | while(cur++ < len && !IS_UTF8_1ST_CHAR(text[cur])); | 356 | while(cursor++ < len && !UTF8_CODEPOINT(text[cursor])); |
| 403 | break; | 357 | break; |
| 404 | } | 358 | } |
| 405 | if(lines > 0) | 359 | else if(lines > 0) |
| 406 | return; | 360 | return; |
| 407 | case XK_Down: | 361 | case XK_Down: |
| 408 | if(!sel || !sel->right) | 362 | if(!sel || !sel->right) |
| @@ -417,7 +371,7 @@ keypress(XKeyEvent *e) { | |||
| 417 | if(!sel) | 371 | if(!sel) |
| 418 | return; | 372 | return; |
| 419 | strncpy(text, sel->text, sizeof text); | 373 | strncpy(text, sel->text, sizeof text); |
| 420 | cur = strlen(text); | 374 | cursor = strlen(text); |
| 421 | match(); | 375 | match(); |
| 422 | break; | 376 | break; |
| 423 | } | 377 | } |
| @@ -427,19 +381,19 @@ keypress(XKeyEvent *e) { | |||
| 427 | void | 381 | void |
| 428 | match(void) { | 382 | match(void) { |
| 429 | unsigned int len; | 383 | unsigned int len; |
| 430 | Item *i, *itemend, *lexact, *lprefix, *lsubstr, *exactend, *prefixend, *substrend; | 384 | Item *item, *itemend, *lexact, *lprefix, *lsubstr, *exactend, *prefixend, *substrend; |
| 431 | 385 | ||
| 432 | len = strlen(text); | 386 | len = strlen(text); |
| 433 | item = lexact = lprefix = lsubstr = itemend = exactend = prefixend = substrend = NULL; | 387 | matches = lexact = lprefix = lsubstr = itemend = exactend = prefixend = substrend = NULL; |
| 434 | for(i = allitems; i; i = i->next) | 388 | for(item = allitems; item; item = item->next) |
| 435 | if(!fstrncmp(text, i->text, len + 1)) | 389 | if(!fstrncmp(text, item->text, len + 1)) |
| 436 | appenditem(i, &lexact, &exactend); | 390 | appenditem(item, &lexact, &exactend); |
| 437 | else if(!fstrncmp(text, i->text, len)) | 391 | else if(!fstrncmp(text, item->text, len)) |
| 438 | appenditem(i, &lprefix, &prefixend); | 392 | appenditem(item, &lprefix, &prefixend); |
| 439 | else if(fstrstr(i->text, text)) | 393 | else if(fstrstr(item->text, text)) |
| 440 | appenditem(i, &lsubstr, &substrend); | 394 | appenditem(item, &lsubstr, &substrend); |
| 441 | if(lexact) { | 395 | if(lexact) { |
| 442 | item = lexact; | 396 | matches = lexact; |
| 443 | itemend = exactend; | 397 | itemend = exactend; |
| 444 | } | 398 | } |
| 445 | if(lprefix) { | 399 | if(lprefix) { |
| @@ -448,7 +402,7 @@ match(void) { | |||
| 448 | lprefix->left = itemend; | 402 | lprefix->left = itemend; |
| 449 | } | 403 | } |
| 450 | else | 404 | else |
| 451 | item = lprefix; | 405 | matches = lprefix; |
| 452 | itemend = prefixend; | 406 | itemend = prefixend; |
| 453 | } | 407 | } |
| 454 | if(lsubstr) { | 408 | if(lsubstr) { |
| @@ -457,36 +411,48 @@ match(void) { | |||
| 457 | lsubstr->left = itemend; | 411 | lsubstr->left = itemend; |
| 458 | } | 412 | } |
| 459 | else | 413 | else |
| 460 | item = lsubstr; | 414 | matches = lsubstr; |
| 461 | } | 415 | } |
| 462 | curr = prev = next = sel = item; | 416 | curr = prev = next = sel = matches; |
| 463 | calcoffsets(); | 417 | calcoffsets(); |
| 464 | } | 418 | } |
| 465 | 419 | ||
| 466 | void | 420 | void |
| 421 | paste(Atom atom) | ||
| 422 | { | ||
| 423 | char *p, *q; | ||
| 424 | int di; | ||
| 425 | unsigned long dl; | ||
| 426 | Atom da; | ||
| 427 | |||
| 428 | XGetWindowProperty(dc.dpy, win, atom, 0, sizeof text - cursor, True, | ||
| 429 | utf8, &da, &di, &dl, &dl, (unsigned char **)&p); | ||
| 430 | insert(p, (q = strchr(p, '\n')) ? q-p : strlen(p)); | ||
| 431 | XFree(p); | ||
| 432 | drawmenu(); | ||
| 433 | } | ||
| 434 | |||
| 435 | void | ||
| 467 | readstdin(void) { | 436 | readstdin(void) { |
| 468 | char *p, buf[sizeof text]; | 437 | char buf[sizeof text]; |
| 469 | unsigned int len = 0, max = 0; | 438 | size_t len; |
| 470 | Item *i, *new; | 439 | Item *item, *new; |
| 471 | 440 | ||
| 472 | i = NULL; | 441 | allitems = NULL; |
| 473 | while(fgets(buf, sizeof buf, stdin)) { | 442 | for(item = NULL; fgets(buf, sizeof buf, stdin); item = new) { |
| 474 | len = strlen(buf); | 443 | len = strlen(buf); |
| 475 | if(buf[len-1] == '\n') | 444 | if(buf[len-1] == '\n') |
| 476 | buf[--len] = '\0'; | 445 | buf[--len] = '\0'; |
| 477 | if(!(p = strdup(buf))) | ||
| 478 | eprint("cannot strdup %u bytes\n", len); | ||
| 479 | if((max = MAX(max, len)) == len) | ||
| 480 | maxname = p; | ||
| 481 | if(!(new = malloc(sizeof *new))) | 446 | if(!(new = malloc(sizeof *new))) |
| 482 | eprint("cannot malloc %u bytes\n", sizeof *new); | 447 | eprintf("cannot malloc %u bytes\n", sizeof *new); |
| 448 | if(!(new->text = strdup(buf))) | ||
| 449 | eprintf("cannot strdup %u bytes\n", len); | ||
| 450 | inputw = MAX(inputw, textw(&dc, new->text)); | ||
| 483 | new->next = new->left = new->right = NULL; | 451 | new->next = new->left = new->right = NULL; |
| 484 | new->text = p; | 452 | if(item) |
| 485 | if(!i) | 453 | item->next = new; |
| 486 | allitems = new; | ||
| 487 | else | 454 | else |
| 488 | i->next = new; | 455 | allitems = new; |
| 489 | i = new; | ||
| 490 | } | 456 | } |
| 491 | } | 457 | } |
| 492 | 458 | ||
| @@ -494,8 +460,7 @@ void | |||
| 494 | run(void) { | 460 | run(void) { |
| 495 | XEvent ev; | 461 | XEvent ev; |
| 496 | 462 | ||
| 497 | XSync(dpy, False); | 463 | while(!XNextEvent(dc.dpy, &ev)) |
| 498 | while(!XNextEvent(dpy, &ev)) | ||
| 499 | switch(ev.type) { | 464 | switch(ev.type) { |
| 500 | case Expose: | 465 | case Expose: |
| 501 | if(ev.xexpose.count == 0) | 466 | if(ev.xexpose.count == 0) |
| @@ -504,62 +469,45 @@ run(void) { | |||
| 504 | case KeyPress: | 469 | case KeyPress: |
| 505 | keypress(&ev.xkey); | 470 | keypress(&ev.xkey); |
| 506 | break; | 471 | break; |
| 472 | case SelectionNotify: | ||
| 473 | if(ev.xselection.property != None) | ||
| 474 | paste(ev.xselection.property); | ||
| 475 | break; | ||
| 507 | case VisibilityNotify: | 476 | case VisibilityNotify: |
| 508 | if(ev.xvisibility.state != VisibilityUnobscured) | 477 | if(ev.xvisibility.state != VisibilityUnobscured) |
| 509 | XRaiseWindow(dpy, win); | 478 | XRaiseWindow(dc.dpy, win); |
| 510 | break; | 479 | break; |
| 511 | } | 480 | } |
| 512 | exit(EXIT_FAILURE); | ||
| 513 | } | 481 | } |
| 514 | 482 | ||
| 515 | void | 483 | void |
| 516 | setup(void) { | 484 | setup(void) { |
| 517 | int i, j, x, y; | 485 | int x, y; |
| 518 | #if XINERAMA | 486 | #ifdef XINERAMA |
| 519 | int n; | 487 | int i, n; |
| 520 | XineramaScreenInfo *info = NULL; | 488 | XineramaScreenInfo *info; |
| 521 | #endif | 489 | #endif |
| 522 | XModifierKeymap *modmap; | ||
| 523 | XSetWindowAttributes wa; | 490 | XSetWindowAttributes wa; |
| 524 | 491 | ||
| 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); | 492 | normcol[ColBG] = getcolor(&dc, normbgcolor); |
| 537 | normcol[ColFG] = getcolor(&dc, normfgcolor); | 493 | normcol[ColFG] = getcolor(&dc, normfgcolor); |
| 538 | selcol[ColBG] = getcolor(&dc, selbgcolor); | 494 | selcol[ColBG] = getcolor(&dc, selbgcolor); |
| 539 | selcol[ColFG] = getcolor(&dc, selfgcolor); | 495 | 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 | 496 | ||
| 547 | /* input window geometry */ | 497 | /* input window geometry */ |
| 548 | mh = (dc.font.height + 2) * (lines + 1); | 498 | mh = (dc.font.height + 2) * (lines + 1); |
| 549 | #if XINERAMA | 499 | #ifdef XINERAMA |
| 550 | if(XineramaIsActive(dpy) && (info = XineramaQueryScreens(dpy, &n))) { | 500 | if((info = XineramaQueryScreens(dc.dpy, &n))) { |
| 551 | i = 0; | 501 | int di; |
| 552 | if(n > 1) { | 502 | unsigned int du; |
| 553 | int di; | 503 | Window dw; |
| 554 | unsigned int dui; | 504 | |
| 555 | Window dummy; | 505 | XQueryPointer(dc.dpy, root, &dw, &dw, &x, &y, &di, &di, &du); |
| 556 | if(XQueryPointer(dpy, root, &dummy, &dummy, &x, &y, &di, &di, &dui)) | 506 | for(i = 0; i < n; i++) |
| 557 | for(i = 0; i < n; i++) | 507 | if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height)) |
| 558 | if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height)) | 508 | break; |
| 559 | break; | ||
| 560 | } | ||
| 561 | x = info[i].x_org; | 509 | x = info[i].x_org; |
| 562 | y = topbar ? info[i].y_org : info[i].y_org + info[i].height - mh; | 510 | y = info[i].y_org + (topbar ? 0 : info[i].height - mh); |
| 563 | mw = info[i].width; | 511 | mw = info[i].width; |
| 564 | XFree(info); | 512 | XFree(info); |
| 565 | } | 513 | } |
| @@ -567,84 +515,86 @@ setup(void) { | |||
| 567 | #endif | 515 | #endif |
| 568 | { | 516 | { |
| 569 | x = 0; | 517 | x = 0; |
| 570 | y = topbar ? 0 : DisplayHeight(dpy, screen) - mh; | 518 | y = topbar ? 0 : DisplayHeight(dc.dpy, screen) - mh; |
| 571 | mw = DisplayWidth(dpy, screen); | 519 | mw = DisplayWidth(dc.dpy, screen); |
| 572 | } | 520 | } |
| 573 | 521 | ||
| 574 | win = XCreateWindow(dpy, root, x, y, mw, mh, 0, | 522 | /* input window */ |
| 575 | DefaultDepth(dpy, screen), CopyFromParent, | 523 | wa.override_redirect = True; |
| 576 | DefaultVisual(dpy, screen), | 524 | wa.background_pixmap = ParentRelative; |
| 525 | wa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; | ||
| 526 | win = XCreateWindow(dc.dpy, root, x, y, mw, mh, 0, | ||
| 527 | DefaultDepth(dc.dpy, screen), CopyFromParent, | ||
| 528 | DefaultVisual(dc.dpy, screen), | ||
| 577 | CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); | 529 | CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); |
| 578 | 530 | ||
| 531 | match(); | ||
| 532 | grabkeyboard(); | ||
| 579 | setupdraw(&dc, win); | 533 | setupdraw(&dc, win); |
| 580 | if(prompt) | 534 | inputw = MIN(inputw, mw / 3); |
| 581 | promptw = MIN(textw(&dc, prompt), mw / 5); | 535 | utf8 = XInternAtom(dc.dpy, "UTF8_STRING", False); |
| 582 | XMapRaised(dpy, win); | 536 | XMapRaised(dc.dpy, win); |
| 537 | } | ||
| 538 | |||
| 539 | void | ||
| 540 | usage(void) { | ||
| 541 | fputs("usage: dmenu [-b] [-i] [-l <lines>] [-p <prompt>] [-fn <font>] [-nb <color>]\n" | ||
| 542 | " [-nf <color>] [-sb <color>] [-sf <color>] [-v]\n", stderr); | ||
| 543 | exit(EXIT_FAILURE); | ||
| 583 | } | 544 | } |
| 584 | 545 | ||
| 585 | int | 546 | int |
| 586 | main(int argc, char *argv[]) { | 547 | main(int argc, char *argv[]) { |
| 587 | unsigned int i; | 548 | int i; |
| 588 | 549 | ||
| 589 | /* command line args */ | ||
| 590 | progname = "dmenu"; | 550 | progname = "dmenu"; |
| 591 | for(i = 1; i < argc; i++) | 551 | for(i = 1; i < argc; i++) |
| 592 | if(!strcmp(argv[i], "-i")) { | 552 | /* 1-arg flags */ |
| 593 | fstrncmp = strncasecmp; | 553 | if(!strcmp(argv[i], "-v")) { |
| 594 | fstrstr = cistrstr; | 554 | fputs("dmenu-"VERSION", © 2006-2010 dmenu engineers, see LICENSE for details\n", stdout); |
| 555 | exit(EXIT_SUCCESS); | ||
| 595 | } | 556 | } |
| 596 | else if(!strcmp(argv[i], "-b")) | 557 | else if(!strcmp(argv[i], "-b")) |
| 597 | topbar = False; | 558 | topbar = False; |
| 559 | else if(!strcmp(argv[i], "-i")) { | ||
| 560 | fstrncmp = strncasecmp; | ||
| 561 | fstrstr = cistrstr; | ||
| 562 | } | ||
| 563 | else if(i == argc-1) | ||
| 564 | usage(); | ||
| 565 | /* 2-arg flags */ | ||
| 598 | else if(!strcmp(argv[i], "-l")) { | 566 | else if(!strcmp(argv[i], "-l")) { |
| 599 | if(++i < argc) lines = atoi(argv[i]); | 567 | if((lines = atoi(argv[++i])) > 0) |
| 600 | if(lines > 0) | ||
| 601 | calcoffsets = calcoffsetsv; | 568 | calcoffsets = calcoffsetsv; |
| 602 | } | 569 | } |
| 603 | else if(!strcmp(argv[i], "-fn")) { | ||
| 604 | if(++i < argc) font = argv[i]; | ||
| 605 | } | ||
| 606 | else if(!strcmp(argv[i], "-nb")) { | ||
| 607 | if(++i < argc) normbgcolor = argv[i]; | ||
| 608 | } | ||
| 609 | else if(!strcmp(argv[i], "-nf")) { | ||
| 610 | if(++i < argc) normfgcolor = argv[i]; | ||
| 611 | } | ||
| 612 | else if(!strcmp(argv[i], "-p")) { | 570 | else if(!strcmp(argv[i], "-p")) { |
| 613 | if(++i < argc) prompt = argv[i]; | 571 | prompt = argv[++i]; |
| 614 | } | 572 | promptw = MIN(textw(&dc, prompt), mw/5); |
| 615 | else if(!strcmp(argv[i], "-sb")) { | ||
| 616 | if(++i < argc) selbgcolor = argv[i]; | ||
| 617 | } | ||
| 618 | else if(!strcmp(argv[i], "-sf")) { | ||
| 619 | if(++i < argc) selfgcolor = argv[i]; | ||
| 620 | } | ||
| 621 | else if(!strcmp(argv[i], "-v")) { | ||
| 622 | printf("dmenu-"VERSION", © 2006-2010 dmenu engineers, see LICENSE for details\n"); | ||
| 623 | exit(EXIT_SUCCESS); | ||
| 624 | } | ||
| 625 | else { | ||
| 626 | fputs("usage: dmenu [-i] [-b] [-l <lines>] [-fn <font>] [-nb <color>]\n" | ||
| 627 | " [-nf <color>] [-p <prompt>] [-sb <color>] [-sf <color>] [-v]\n", stderr); | ||
| 628 | exit(EXIT_FAILURE); | ||
| 629 | } | 573 | } |
| 574 | else if(!strcmp(argv[i], "-fn")) | ||
| 575 | font = argv[++i]; | ||
| 576 | else if(!strcmp(argv[i], "-nb")) | ||
| 577 | normbgcolor = argv[++i]; | ||
| 578 | else if(!strcmp(argv[i], "-nf")) | ||
| 579 | normfgcolor = argv[++i]; | ||
| 580 | else if(!strcmp(argv[i], "-sb")) | ||
| 581 | selbgcolor = argv[++i]; | ||
| 582 | else if(!strcmp(argv[i], "-sf")) | ||
| 583 | selfgcolor = argv[++i]; | ||
| 584 | else | ||
| 585 | usage(); | ||
| 586 | |||
| 630 | if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) | 587 | if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) |
| 631 | fprintf(stderr, "dmenu: warning: no locale support\n"); | 588 | fputs("dmenu: warning: no locale support\n", stderr); |
| 632 | if(!(dpy = XOpenDisplay(NULL))) | 589 | if(!(dc.dpy = XOpenDisplay(NULL))) |
| 633 | eprint("cannot open display\n"); | 590 | eprintf("cannot open display\n"); |
| 634 | if(atexit(&cleanup) != 0) | 591 | screen = DefaultScreen(dc.dpy); |
| 635 | eprint("cannot register cleanup\n"); | 592 | root = RootWindow(dc.dpy, screen); |
| 636 | screen = DefaultScreen(dpy); | 593 | initfont(&dc, font); |
| 637 | root = RootWindow(dpy, screen); | ||
| 638 | if(!(argp = malloc(sizeof *argp * (argc+2)))) | ||
| 639 | eprint("cannot malloc %u bytes\n", sizeof *argp * (argc+2)); | ||
| 640 | memcpy(argp + 2, argv + 1, sizeof *argp * argc); | ||
| 641 | 594 | ||
| 642 | readstdin(); | 595 | readstdin(); |
| 643 | grabkeyboard(); | ||
| 644 | setup(); | 596 | setup(); |
| 645 | if(maxname) | ||
| 646 | cmdw = MIN(textw(&dc, maxname), mw / 3); | ||
| 647 | match(); | ||
| 648 | run(); | 597 | run(); |
| 649 | return 0; | 598 | |
| 599 | return EXIT_FAILURE; /* should not reach */ | ||
| 650 | } | 600 | } |
