| 6 |
6 |
|
#include <string.h> |
| 7 |
7 |
|
#include <unistd.h> |
| 8 |
8 |
|
#include <X11/keysym.h> |
|
9 |
+ |
#include <X11/Xatom.h> |
| 9 |
10 |
|
#include <X11/Xlib.h> |
| 10 |
11 |
|
#include <X11/Xutil.h> |
| 11 |
12 |
|
#ifdef XINERAMA |
|
| 14 |
15 |
|
#include <draw.h> |
| 15 |
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 |
19 |
|
#define MIN(a,b) ((a) < (b) ? (a) : (b)) |
| 19 |
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 |
23 |
|
typedef struct Item Item; |
| 23 |
24 |
|
struct Item { |
| 24 |
25 |
|
char *text; |
| 25 |
|
- |
Item *next; /* traverses all items */ |
| 26 |
|
- |
Item *left, *right; /* traverses items matching current search pattern */ |
|
26 |
+ |
Item *next; /* traverses all items */ |
|
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 |
31 |
|
static void calcoffsetsh(void); |
| 31 |
32 |
|
static void calcoffsetsv(void); |
| 32 |
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 |
34 |
|
static void drawmenu(void); |
| 36 |
35 |
|
static void drawmenuh(void); |
| 37 |
36 |
|
static void drawmenuv(void); |
| 38 |
37 |
|
static void grabkeyboard(void); |
|
38 |
+ |
static void insert(const char *s, ssize_t n); |
| 39 |
39 |
|
static void keypress(XKeyEvent *e); |
| 40 |
40 |
|
static void match(void); |
|
41 |
+ |
static void paste(Atom atom); |
| 41 |
42 |
|
static void readstdin(void); |
| 42 |
43 |
|
static void run(void); |
| 43 |
44 |
|
static void setup(void); |
|
45 |
+ |
static void usage(void); |
| 44 |
46 |
|
|
| 45 |
|
- |
static char **argp = NULL; |
| 46 |
|
- |
static char *maxname = NULL; |
| 47 |
47 |
|
static char *prompt; |
| 48 |
48 |
|
static char text[4096]; |
| 49 |
|
- |
static int promptw; |
| 50 |
49 |
|
static int screen; |
| 51 |
|
- |
static size_t cur = 0; |
| 52 |
|
- |
static unsigned int cmdw = 0; |
|
50 |
+ |
static size_t cursor = 0; |
|
51 |
+ |
static unsigned int inputw = 0; |
| 53 |
52 |
|
static unsigned int lines = 0; |
| 54 |
|
- |
static unsigned int numlockmask; |
| 55 |
53 |
|
static unsigned int mw, mh; |
|
54 |
+ |
static unsigned int promptw = 0; |
| 56 |
55 |
|
static unsigned long normcol[ColLast]; |
| 57 |
56 |
|
static unsigned long selcol[ColLast]; |
|
57 |
+ |
static Atom utf8; |
| 58 |
58 |
|
static Bool topbar = True; |
| 59 |
59 |
|
static DC dc; |
| 60 |
|
- |
static Display *dpy; |
| 61 |
|
- |
static Item *allitems = NULL; /* first of all items */ |
| 62 |
|
- |
static Item *item = NULL; /* first of pattern matching items */ |
| 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; |
|
60 |
+ |
static Item *allitems, *matches; |
|
61 |
+ |
static Item *curr, *prev, *next, *sel; |
|
62 |
+ |
static Window root, win; |
| 68 |
63 |
|
|
| 69 |
64 |
|
static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; |
| 70 |
65 |
|
static char *(*fstrstr)(const char *, const char *) = strstr; |
| 71 |
66 |
|
static void (*calcoffsets)(void) = calcoffsetsh; |
| 72 |
67 |
|
|
| 73 |
68 |
|
void |
| 74 |
|
- |
appenditem(Item *i, Item **list, Item **last) { |
|
69 |
+ |
appenditem(Item *item, Item **list, Item **last) { |
| 75 |
70 |
|
if(!(*last)) |
| 76 |
|
- |
*list = i; |
|
71 |
+ |
*list = item; |
| 77 |
72 |
|
else |
| 78 |
|
- |
(*last)->right = i; |
| 79 |
|
- |
i->left = *last; |
| 80 |
|
- |
i->right = NULL; |
| 81 |
|
- |
*last = i; |
|
73 |
+ |
(*last)->right = item; |
|
74 |
+ |
item->left = *last; |
|
75 |
+ |
item->right = NULL; |
|
76 |
+ |
*last = item; |
| 82 |
77 |
|
} |
| 83 |
78 |
|
|
| 84 |
79 |
|
void |
| 85 |
80 |
|
calcoffsetsh(void) { |
| 86 |
81 |
|
unsigned int w, x; |
| 87 |
82 |
|
|
| 88 |
|
- |
w = promptw + cmdw + textw(&dc, "<") + textw(&dc, ">"); |
|
83 |
+ |
w = promptw + inputw + textw(&dc, "<") + textw(&dc, ">"); |
| 89 |
84 |
|
for(x = w, next = curr; next; next = next->right) |
| 90 |
85 |
|
if((x += MIN(textw(&dc, next->text), mw / 3)) > mw) |
| 91 |
86 |
|
break; |
|
| 129 |
124 |
|
} |
| 130 |
125 |
|
|
| 131 |
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 |
127 |
|
drawmenu(void) { |
| 160 |
128 |
|
dc.x = 0; |
| 161 |
129 |
|
dc.y = 0; |
|
| 172 |
140 |
|
dc.x += dc.w; |
| 173 |
141 |
|
} |
| 174 |
142 |
|
dc.w = mw - dc.x; |
| 175 |
|
- |
/* print command */ |
| 176 |
|
- |
if(cmdw && item && lines == 0) |
| 177 |
|
- |
dc.w = cmdw; |
|
143 |
+ |
/* print input area */ |
|
144 |
+ |
if(matches && lines == 0 && textw(&dc, text) <= inputw) |
|
145 |
+ |
dc.w = inputw; |
| 178 |
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 |
148 |
|
if(lines > 0) |
| 181 |
149 |
|
drawmenuv(); |
| 182 |
|
- |
else if(curr) |
|
150 |
+ |
else if(curr && (dc.w == inputw || curr->next)) |
| 183 |
151 |
|
drawmenuh(); |
| 184 |
152 |
|
commitdraw(&dc, win); |
| 185 |
153 |
|
} |
| 186 |
154 |
|
|
| 187 |
155 |
|
void |
| 188 |
156 |
|
drawmenuh(void) { |
| 189 |
|
- |
Item *i; |
|
157 |
+ |
Item *item; |
| 190 |
158 |
|
|
| 191 |
|
- |
dc.x += cmdw; |
|
159 |
+ |
dc.x += inputw; |
| 192 |
160 |
|
dc.w = textw(&dc, "<"); |
| 193 |
|
- |
drawtext(&dc, curr->left ? "<" : NULL, normcol); |
|
161 |
+ |
if(curr->left) |
|
162 |
+ |
drawtext(&dc, "<", normcol); |
| 194 |
163 |
|
dc.x += dc.w; |
| 195 |
|
- |
for(i = curr; i != next; i = i->right) { |
| 196 |
|
- |
dc.w = MIN(textw(&dc, i->text), mw / 3); |
| 197 |
|
- |
drawitem(i->text, (sel == i) ? selcol : normcol); |
|
164 |
+ |
for(item = curr; item != next; item = item->right) { |
|
165 |
+ |
dc.w = MIN(textw(&dc, item->text), mw / 3); |
|
166 |
+ |
if(item == sel) |
|
167 |
+ |
drawbox(&dc, selcol); |
|
168 |
+ |
drawtext(&dc, item->text, (item == sel) ? selcol : normcol); |
| 198 |
169 |
|
dc.x += dc.w; |
| 199 |
170 |
|
} |
| 200 |
171 |
|
dc.w = textw(&dc, ">"); |
| 201 |
172 |
|
dc.x = mw - dc.w; |
| 202 |
|
- |
drawtext(&dc, next ? ">" : NULL, normcol); |
|
173 |
+ |
if(next) |
|
174 |
+ |
drawtext(&dc, ">", normcol); |
| 203 |
175 |
|
} |
| 204 |
176 |
|
|
| 205 |
177 |
|
void |
| 206 |
178 |
|
drawmenuv(void) { |
| 207 |
|
- |
Item *i; |
|
179 |
+ |
Item *item; |
| 208 |
180 |
|
XWindowAttributes wa; |
| 209 |
181 |
|
|
| 210 |
182 |
|
dc.y = topbar ? dc.h : 0; |
| 211 |
183 |
|
dc.w = mw - dc.x; |
| 212 |
|
- |
for(i = curr; i != next; i = i->right) { |
| 213 |
|
- |
drawitem(i->text, (sel == i) ? selcol : normcol); |
|
184 |
+ |
for(item = curr; item != next; item = item->right) { |
|
185 |
+ |
if(item == sel) |
|
186 |
+ |
drawbox(&dc, selcol); |
|
187 |
+ |
drawtext(&dc, item->text, (item == sel) ? selcol : normcol); |
| 214 |
188 |
|
dc.y += dc.h; |
| 215 |
189 |
|
} |
| 216 |
|
- |
if(!XGetWindowAttributes(dpy, win, &wa)) |
| 217 |
|
- |
eprint("cannot get window attributes"); |
| 218 |
|
- |
XMoveResizeWindow(dpy, win, wa.x, wa.y + (topbar ? 0 : wa.height - mh), mw, mh); |
|
190 |
+ |
if(!XGetWindowAttributes(dc.dpy, win, &wa)) |
|
191 |
+ |
eprintf("cannot get window attributes\n"); |
|
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 |
196 |
|
void |
| 222 |
197 |
|
grabkeyboard(void) { |
| 223 |
|
- |
unsigned int n; |
|
198 |
+ |
int i; |
| 224 |
199 |
|
|
| 225 |
|
- |
for(n = 0; n < 1000; n++) { |
| 226 |
|
- |
if(!XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)) |
|
200 |
+ |
for(i = 0; i < 1000; i++) { |
|
201 |
+ |
if(!XGrabKeyboard(dc.dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)) |
| 227 |
202 |
|
return; |
| 228 |
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 |
217 |
|
void |
| 234 |
218 |
|
keypress(XKeyEvent *e) { |
| 235 |
219 |
|
char buf[sizeof text]; |
| 236 |
|
- |
int num; |
| 237 |
|
- |
unsigned int i, len; |
|
220 |
+ |
int n; |
|
221 |
+ |
size_t len; |
| 238 |
222 |
|
KeySym ksym; |
| 239 |
223 |
|
|
| 240 |
224 |
|
len = strlen(text); |
| 241 |
|
- |
num = 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 */ |
|
225 |
+ |
XLookupString(e, buf, sizeof buf, &ksym, NULL); |
| 251 |
226 |
|
if(e->state & ControlMask) { |
| 252 |
227 |
|
switch(tolower(ksym)) { |
| 253 |
228 |
|
default: |
|
| 277 |
252 |
|
case XK_m: |
| 278 |
253 |
|
ksym = XK_Return; |
| 279 |
254 |
|
break; |
| 280 |
|
- |
case XK_k: |
| 281 |
|
- |
text[cur] = '\0'; |
|
255 |
+ |
case XK_k: /* delete right */ |
|
256 |
+ |
text[cursor] = '\0'; |
| 282 |
257 |
|
break; |
| 283 |
258 |
|
case XK_n: |
| 284 |
259 |
|
ksym = XK_Down; |
|
| 286 |
261 |
|
case XK_p: |
| 287 |
262 |
|
ksym = XK_Up; |
| 288 |
263 |
|
break; |
| 289 |
|
- |
case XK_u: |
| 290 |
|
- |
memmove(text, text + cur, sizeof text - cur + 1); |
| 291 |
|
- |
cur = 0; |
| 292 |
|
- |
match(); |
|
264 |
+ |
case XK_u: /* delete left */ |
|
265 |
+ |
insert(NULL, -cursor); |
| 293 |
266 |
|
break; |
| 294 |
|
- |
case XK_w: |
| 295 |
|
- |
if(cur == 0) |
|
267 |
+ |
case XK_w: /* delete word */ |
|
268 |
+ |
if(cursor == 0) |
| 296 |
269 |
|
return; |
| 297 |
|
- |
i = cur; |
| 298 |
|
- |
while(i-- > 0 && text[i] == ' '); |
| 299 |
|
- |
while(i-- > 0 && text[i] != ' '); |
| 300 |
|
- |
memmove(text + i + 1, text + cur, sizeof text - cur + 1); |
| 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'; |
|
270 |
+ |
n = 0; |
|
271 |
+ |
while(cursor - n++ > 0 && text[cursor - n] == ' '); |
|
272 |
+ |
while(cursor - n++ > 0 && text[cursor - n] != ' '); |
|
273 |
+ |
insert(NULL, -(--n)); |
| 318 |
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 |
281 |
|
switch(ksym) { |
| 322 |
282 |
|
default: |
| 323 |
|
- |
num = MIN(num, sizeof text); |
| 324 |
|
- |
if(num && !iscntrl((int) buf[0])) { |
| 325 |
|
- |
memmove(text + cur + num, text + cur, sizeof text - cur - num); |
| 326 |
|
- |
memcpy(text + cur, buf, num); |
| 327 |
|
- |
cur += num; |
| 328 |
|
- |
match(); |
| 329 |
|
- |
} |
|
283 |
+ |
if(!iscntrl((int)*buf)) |
|
284 |
+ |
insert(buf, MIN(strlen(buf), sizeof text - cursor)); |
| 330 |
285 |
|
break; |
| 331 |
286 |
|
case XK_BackSpace: |
| 332 |
|
- |
if(cur == 0) |
|
287 |
+ |
if(cursor == 0) |
| 333 |
288 |
|
return; |
| 334 |
|
- |
for(i = 1; len - i > 0 && !IS_UTF8_1ST_CHAR(text[cur - i]); i++); |
| 335 |
|
- |
memmove(text + cur - i, text + cur, sizeof text - cur + i); |
| 336 |
|
- |
cur -= i; |
| 337 |
|
- |
match(); |
|
289 |
+ |
for(n = 1; cursor - n > 0 && !UTF8_CODEPOINT(text[cursor - n]); n++); |
|
290 |
+ |
insert(NULL, -n); |
| 338 |
291 |
|
break; |
| 339 |
292 |
|
case XK_Delete: |
| 340 |
|
- |
if(cur == len) |
|
293 |
+ |
if(cursor == len) |
| 341 |
294 |
|
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); |
| 344 |
|
- |
match(); |
|
295 |
+ |
for(n = 1; cursor + n < len && !UTF8_CODEPOINT(text[cursor + n]); n++); |
|
296 |
+ |
cursor += n; |
|
297 |
+ |
insert(NULL, -n); |
| 345 |
298 |
|
break; |
| 346 |
299 |
|
case XK_End: |
| 347 |
|
- |
if(cur < len) { |
| 348 |
|
- |
cur = len; |
|
300 |
+ |
if(cursor < len) { |
|
301 |
+ |
cursor = len; |
| 349 |
302 |
|
break; |
| 350 |
303 |
|
} |
| 351 |
304 |
|
while(next) { |
|
| 358 |
311 |
|
case XK_Escape: |
| 359 |
312 |
|
exit(EXIT_FAILURE); |
| 360 |
313 |
|
case XK_Home: |
| 361 |
|
- |
if(sel == item) { |
| 362 |
|
- |
cur = 0; |
|
314 |
+ |
if(sel == matches) { |
|
315 |
+ |
cursor = 0; |
| 363 |
316 |
|
break; |
| 364 |
317 |
|
} |
| 365 |
|
- |
sel = curr = item; |
|
318 |
+ |
sel = curr = matches; |
| 366 |
319 |
|
calcoffsets(); |
| 367 |
320 |
|
break; |
| 368 |
321 |
|
case XK_Left: |
| 369 |
|
- |
if(cur > 0 && (!sel || !sel->left || lines > 0)) { |
| 370 |
|
- |
while(cur-- > 0 && !IS_UTF8_1ST_CHAR(text[cur])); |
|
322 |
+ |
if(cursor > 0 && (!sel || !sel->left || lines > 0)) { |
|
323 |
+ |
while(cursor-- > 0 && !UTF8_CODEPOINT(text[cursor])); |
| 371 |
324 |
|
break; |
| 372 |
325 |
|
} |
| 373 |
|
- |
if(lines > 0) |
|
326 |
+ |
else if(lines > 0) |
| 374 |
327 |
|
return; |
| 375 |
328 |
|
case XK_Up: |
| 376 |
329 |
|
if(!sel || !sel->left) |
|
| 394 |
347 |
|
calcoffsets(); |
| 395 |
348 |
|
break; |
| 396 |
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 |
352 |
|
fflush(stdout); |
| 399 |
353 |
|
exit(EXIT_SUCCESS); |
| 400 |
354 |
|
case XK_Right: |
| 401 |
|
- |
if(cur < len) { |
| 402 |
|
- |
while(cur++ < len && !IS_UTF8_1ST_CHAR(text[cur])); |
|
355 |
+ |
if(cursor < len) { |
|
356 |
+ |
while(cursor++ < len && !UTF8_CODEPOINT(text[cursor])); |
| 403 |
357 |
|
break; |
| 404 |
358 |
|
} |
| 405 |
|
- |
if(lines > 0) |
|
359 |
+ |
else if(lines > 0) |
| 406 |
360 |
|
return; |
| 407 |
361 |
|
case XK_Down: |
| 408 |
362 |
|
if(!sel || !sel->right) |
|
| 417 |
371 |
|
if(!sel) |
| 418 |
372 |
|
return; |
| 419 |
373 |
|
strncpy(text, sel->text, sizeof text); |
| 420 |
|
- |
cur = strlen(text); |
|
374 |
+ |
cursor = strlen(text); |
| 421 |
375 |
|
match(); |
| 422 |
376 |
|
break; |
| 423 |
377 |
|
} |
|
| 427 |
381 |
|
void |
| 428 |
382 |
|
match(void) { |
| 429 |
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 |
386 |
|
len = strlen(text); |
| 433 |
|
- |
item = lexact = lprefix = lsubstr = itemend = exactend = prefixend = substrend = NULL; |
| 434 |
|
- |
for(i = allitems; i; i = i->next) |
| 435 |
|
- |
if(!fstrncmp(text, i->text, len + 1)) |
| 436 |
|
- |
appenditem(i, &lexact, &exactend); |
| 437 |
|
- |
else if(!fstrncmp(text, i->text, len)) |
| 438 |
|
- |
appenditem(i, &lprefix, &prefixend); |
| 439 |
|
- |
else if(fstrstr(i->text, text)) |
| 440 |
|
- |
appenditem(i, &lsubstr, &substrend); |
|
387 |
+ |
matches = lexact = lprefix = lsubstr = itemend = exactend = prefixend = substrend = NULL; |
|
388 |
+ |
for(item = allitems; item; item = item->next) |
|
389 |
+ |
if(!fstrncmp(text, item->text, len + 1)) |
|
390 |
+ |
appenditem(item, &lexact, &exactend); |
|
391 |
+ |
else if(!fstrncmp(text, item->text, len)) |
|
392 |
+ |
appenditem(item, &lprefix, &prefixend); |
|
393 |
+ |
else if(fstrstr(item->text, text)) |
|
394 |
+ |
appenditem(item, &lsubstr, &substrend); |
| 441 |
395 |
|
if(lexact) { |
| 442 |
|
- |
item = lexact; |
|
396 |
+ |
matches = lexact; |
| 443 |
397 |
|
itemend = exactend; |
| 444 |
398 |
|
} |
| 445 |
399 |
|
if(lprefix) { |
|
| 448 |
402 |
|
lprefix->left = itemend; |
| 449 |
403 |
|
} |
| 450 |
404 |
|
else |
| 451 |
|
- |
item = lprefix; |
|
405 |
+ |
matches = lprefix; |
| 452 |
406 |
|
itemend = prefixend; |
| 453 |
407 |
|
} |
| 454 |
408 |
|
if(lsubstr) { |
|
| 457 |
411 |
|
lsubstr->left = itemend; |
| 458 |
412 |
|
} |
| 459 |
413 |
|
else |
| 460 |
|
- |
item = lsubstr; |
|
414 |
+ |
matches = lsubstr; |
| 461 |
415 |
|
} |
| 462 |
|
- |
curr = prev = next = sel = item; |
|
416 |
+ |
curr = prev = next = sel = matches; |
| 463 |
417 |
|
calcoffsets(); |
| 464 |
418 |
|
} |
| 465 |
419 |
|
|
| 466 |
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 |
436 |
|
readstdin(void) { |
| 468 |
|
- |
char *p, buf[sizeof text]; |
| 469 |
|
- |
unsigned int len = 0, max = 0; |
| 470 |
|
- |
Item *i, *new; |
|
437 |
+ |
char buf[sizeof text]; |
|
438 |
+ |
size_t len; |
|
439 |
+ |
Item *item, *new; |
| 471 |
440 |
|
|
| 472 |
|
- |
i = NULL; |
| 473 |
|
- |
while(fgets(buf, sizeof buf, stdin)) { |
|
441 |
+ |
allitems = NULL; |
|
442 |
+ |
for(item = NULL; fgets(buf, sizeof buf, stdin); item = new) { |
| 474 |
443 |
|
len = strlen(buf); |
| 475 |
444 |
|
if(buf[len-1] == '\n') |
| 476 |
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 |
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 |
451 |
|
new->next = new->left = new->right = NULL; |
| 484 |
|
- |
new->text = p; |
| 485 |
|
- |
if(!i) |
| 486 |
|
- |
allitems = new; |
|
452 |
+ |
if(item) |
|
453 |
+ |
item->next = new; |
| 487 |
454 |
|
else |
| 488 |
|
- |
i->next = new; |
| 489 |
|
- |
i = new; |
|
455 |
+ |
allitems = new; |
| 490 |
456 |
|
} |
| 491 |
457 |
|
} |
| 492 |
458 |
|
|
|
| 494 |
460 |
|
run(void) { |
| 495 |
461 |
|
XEvent ev; |
| 496 |
462 |
|
|
| 497 |
|
- |
XSync(dpy, False); |
| 498 |
|
- |
while(!XNextEvent(dpy, &ev)) |
|
463 |
+ |
while(!XNextEvent(dc.dpy, &ev)) |
| 499 |
464 |
|
switch(ev.type) { |
| 500 |
465 |
|
case Expose: |
| 501 |
466 |
|
if(ev.xexpose.count == 0) |
|
| 504 |
469 |
|
case KeyPress: |
| 505 |
470 |
|
keypress(&ev.xkey); |
| 506 |
471 |
|
break; |
|
472 |
+ |
case SelectionNotify: |
|
473 |
+ |
if(ev.xselection.property != None) |
|
474 |
+ |
paste(ev.xselection.property); |
|
475 |
+ |
break; |
| 507 |
476 |
|
case VisibilityNotify: |
| 508 |
477 |
|
if(ev.xvisibility.state != VisibilityUnobscured) |
| 509 |
|
- |
XRaiseWindow(dpy, win); |
|
478 |
+ |
XRaiseWindow(dc.dpy, win); |
| 510 |
479 |
|
break; |
| 511 |
480 |
|
} |
| 512 |
|
- |
exit(EXIT_FAILURE); |
| 513 |
481 |
|
} |
| 514 |
482 |
|
|
| 515 |
483 |
|
void |
| 516 |
484 |
|
setup(void) { |
| 517 |
|
- |
int i, j, x, y; |
| 518 |
|
- |
#if XINERAMA |
| 519 |
|
- |
int n; |
| 520 |
|
- |
XineramaScreenInfo *info = NULL; |
|
485 |
+ |
int x, y; |
|
486 |
+ |
#ifdef XINERAMA |
|
487 |
+ |
int i, n; |
|
488 |
+ |
XineramaScreenInfo *info; |
| 521 |
489 |
|
#endif |
| 522 |
|
- |
XModifierKeymap *modmap; |
| 523 |
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 |
492 |
|
normcol[ColBG] = getcolor(&dc, normbgcolor); |
| 537 |
493 |
|
normcol[ColFG] = getcolor(&dc, normfgcolor); |
| 538 |
494 |
|
selcol[ColBG] = getcolor(&dc, selbgcolor); |
| 539 |
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 |
497 |
|
/* input window geometry */ |
| 548 |
498 |
|
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 |
|
- |
} |
|
499 |
+ |
#ifdef XINERAMA |
|
500 |
+ |
if((info = XineramaQueryScreens(dc.dpy, &n))) { |
|
501 |
+ |
int di; |
|
502 |
+ |
unsigned int du; |
|
503 |
+ |
Window dw; |
|
504 |
+ |
|
|
505 |
+ |
XQueryPointer(dc.dpy, root, &dw, &dw, &x, &y, &di, &di, &du); |
|
506 |
+ |
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)) |
|
508 |
+ |
break; |
| 561 |
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 |
511 |
|
mw = info[i].width; |
| 564 |
512 |
|
XFree(info); |
| 565 |
513 |
|
} |
|
| 567 |
515 |
|
#endif |
| 568 |
516 |
|
{ |
| 569 |
517 |
|
x = 0; |
| 570 |
|
- |
y = topbar ? 0 : DisplayHeight(dpy, screen) - mh; |
| 571 |
|
- |
mw = DisplayWidth(dpy, screen); |
|
518 |
+ |
y = topbar ? 0 : DisplayHeight(dc.dpy, screen) - mh; |
|
519 |
+ |
mw = DisplayWidth(dc.dpy, screen); |
| 572 |
520 |
|
} |
| 573 |
521 |
|
|
| 574 |
|
- |
win = XCreateWindow(dpy, root, x, y, mw, mh, 0, |
| 575 |
|
- |
DefaultDepth(dpy, screen), CopyFromParent, |
| 576 |
|
- |
DefaultVisual(dpy, screen), |
|
522 |
+ |
/* input window */ |
|
523 |
+ |
wa.override_redirect = True; |
|
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 |
529 |
|
CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); |
| 578 |
530 |
|
|
|
531 |
+ |
match(); |
|
532 |
+ |
grabkeyboard(); |
| 579 |
533 |
|
setupdraw(&dc, win); |
| 580 |
|
- |
if(prompt) |
| 581 |
|
- |
promptw = MIN(textw(&dc, prompt), mw / 5); |
| 582 |
|
- |
XMapRaised(dpy, win); |
|
534 |
+ |
inputw = MIN(inputw, mw / 3); |
|
535 |
+ |
utf8 = XInternAtom(dc.dpy, "UTF8_STRING", False); |
|
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 |
546 |
|
int |
| 586 |
547 |
|
main(int argc, char *argv[]) { |
| 587 |
|
- |
unsigned int i; |
|
548 |
+ |
int i; |
| 588 |
549 |
|
|
| 589 |
|
- |
/* command line args */ |
| 590 |
550 |
|
progname = "dmenu"; |
| 591 |
551 |
|
for(i = 1; i < argc; i++) |
| 592 |
|
- |
if(!strcmp(argv[i], "-i")) { |
| 593 |
|
- |
fstrncmp = strncasecmp; |
| 594 |
|
- |
fstrstr = cistrstr; |
|
552 |
+ |
/* 1-arg flags */ |
|
553 |
+ |
if(!strcmp(argv[i], "-v")) { |
|
554 |
+ |
fputs("dmenu-"VERSION", © 2006-2010 dmenu engineers, see LICENSE for details\n", stdout); |
|
555 |
+ |
exit(EXIT_SUCCESS); |
| 595 |
556 |
|
} |
| 596 |
557 |
|
else if(!strcmp(argv[i], "-b")) |
| 597 |
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 |
566 |
|
else if(!strcmp(argv[i], "-l")) { |
| 599 |
|
- |
if(++i < argc) lines = atoi(argv[i]); |
| 600 |
|
- |
if(lines > 0) |
|
567 |
+ |
if((lines = atoi(argv[++i])) > 0) |
| 601 |
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 |
570 |
|
else if(!strcmp(argv[i], "-p")) { |
| 613 |
|
- |
if(++i < argc) prompt = argv[i]; |
|
571 |
+ |
prompt = argv[++i]; |
|
572 |
+ |
promptw = MIN(textw(&dc, prompt), mw/5); |
| 614 |
573 |
|
} |
| 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 |
|
- |
} |
|
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 |
587 |
|
if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) |
| 631 |
|
- |
fprintf(stderr, "dmenu: warning: no locale support\n"); |
| 632 |
|
- |
if(!(dpy = XOpenDisplay(NULL))) |
| 633 |
|
- |
eprint("cannot open display\n"); |
| 634 |
|
- |
if(atexit(&cleanup) != 0) |
| 635 |
|
- |
eprint("cannot register cleanup\n"); |
| 636 |
|
- |
screen = DefaultScreen(dpy); |
| 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); |
|
588 |
+ |
fputs("dmenu: warning: no locale support\n", stderr); |
|
589 |
+ |
if(!(dc.dpy = XOpenDisplay(NULL))) |
|
590 |
+ |
eprintf("cannot open display\n"); |
|
591 |
+ |
screen = DefaultScreen(dc.dpy); |
|
592 |
+ |
root = RootWindow(dc.dpy, screen); |
|
593 |
+ |
initfont(&dc, font); |
| 641 |
594 |
|
|
| 642 |
595 |
|
readstdin(); |
| 643 |
|
- |
grabkeyboard(); |
| 644 |
596 |
|
setup(); |
| 645 |
|
- |
if(maxname) |
| 646 |
|
- |
cmdw = MIN(textw(&dc, maxname), mw / 3); |
| 647 |
|
- |
match(); |
| 648 |
597 |
|
run(); |
| 649 |
|
- |
return 0; |
|
598 |
+ |
|
|
599 |
+ |
return EXIT_FAILURE; /* should not reach */ |
| 650 |
600 |
|
} |