Multiple input history
[screen-lua.git] / src / input.c
blob165a4c8ae9f486bff6a3981ec6ce6d37beba5bec
1 /* Copyright (c) 2008, 2009
2 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
4 * Micah Cowan (micah@cowan.name)
5 * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
6 * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
7 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
8 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
9 * Copyright (c) 1987 Oliver Laumann
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3, or (at your option)
14 * any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program (see the file COPYING); if not, see
23 * http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
26 ****************************************************************
29 #include <sys/types.h>
30 #include "config.h"
31 #include "screen.h"
32 #include "extern.h"
34 #define INPUTLINE (flayer->l_height - 1)
36 static void InpProcess __P((char **, int *));
37 static void InpAbort __P((void));
38 static void InpRedisplayLine __P((int, int, int, int));
40 extern struct layer *flayer;
41 extern struct display *display;
42 extern struct mchar mchar_blank, mchar_so;
44 struct inpline
46 char buf[101]; /* text buffer */
47 int len; /* length of the editible string */
48 int pos; /* cursor position in editable string */
49 struct inpline *next, *prev;
52 /* 'inphist' is used to store the current input when scrolling through history.
53 * inpline->prev == history-prev
54 * inpline->next == history-next
56 static struct inpline inphist;
58 struct inpdata
60 struct inpline inp;
61 int inpmaxlen; /* 100, or less, if caller has shorter buffer */
62 char *inpstring; /* the prompt */
63 int inpstringlen; /* length of the prompt */
64 int inpmode; /* INP_NOECHO, INP_RAW, INP_EVERY */
65 void (*inpfinfunc) __P((char *buf, int len, char *priv));
66 char *priv; /* private data for finfunc */
67 int privdata; /* private data space */
70 static struct LayFuncs InpLf =
72 InpProcess,
73 InpAbort,
74 InpRedisplayLine,
75 DefClearLine,
76 DefRewrite,
77 DefResize,
78 DefRestore
82 ** Here is the input routine
85 /* called once, after InitOverlayPage in Input() or Isearch() */
86 void
87 inp_setprompt(p, s)
88 char *p, *s;
90 struct inpdata *inpdata;
92 inpdata = (struct inpdata *)flayer->l_data;
93 if (p)
95 inpdata->inpstringlen = strlen(p);
96 inpdata->inpstring = p;
98 if (s)
100 if (s != inpdata->inp.buf)
101 strncpy(inpdata->inp.buf, s, sizeof(inpdata->inp.buf) - 1);
102 inpdata->inp.buf[sizeof(inpdata->inp.buf) - 1] = 0;
103 inpdata->inp.pos = inpdata->inp.len = strlen(inpdata->inp.buf);
105 InpRedisplayLine(INPUTLINE, 0, flayer->l_width - 1, 0);
106 flayer->l_x = inpdata->inpstringlen + (inpdata->inpmode & INP_NOECHO ? 0 : inpdata->inp.pos);
107 flayer->l_y = INPUTLINE;
111 * We dont use HS status line with Input().
112 * If we would use it, then we should check e_tgetflag("es") if
113 * we are allowed to use esc sequences there.
115 * mode is an OR of
116 * INP_NOECHO == suppress echoing of characters.
117 * INP_RAW == raw mode. call finfunc after each character typed.
118 * INP_EVERY == digraph mode.
120 void
121 Input(istr, len, mode, finfunc, priv, data)
122 char *istr;
123 int len;
124 int mode;
125 void (*finfunc) __P((char *buf, int len, char *priv));
126 char *priv;
127 int data;
129 int maxlen;
130 struct inpdata *inpdata;
132 if (!flayer)
133 return;
135 if (len > 100)
136 len = 100;
137 if (!(mode & INP_NOECHO))
139 maxlen = flayer->l_width - 1 - strlen(istr);
140 if (len > maxlen)
141 len = maxlen;
143 if (len < 0)
145 LMsg(0, "Width %d chars too small", -len);
146 return;
148 if (InitOverlayPage(sizeof(*inpdata), &InpLf, 1))
149 return;
150 inpdata = (struct inpdata *)flayer->l_data;
151 inpdata->inpmaxlen = len;
152 inpdata->inpfinfunc = finfunc;
153 inpdata->inp.pos = inpdata->inp.len = 0;
154 inpdata->inp.prev = inphist.prev;
155 inpdata->inpmode = mode;
156 inpdata->privdata = data;
157 if (!priv)
158 priv = (char*)&inpdata->privdata;
159 inpdata->priv = priv;
160 inpdata->inpstringlen = 0;
161 inpdata->inpstring = NULL;
162 if (istr)
163 inp_setprompt(istr, (char *)NULL);
166 static void
167 erase_chars(inpdata, from, to, x, mv)
168 struct inpdata *inpdata;
169 char *from;
170 char *to;
171 int x;
172 int mv;
174 int chng;
175 ASSERT(from < to);
176 if (inpdata->inp.len > to - inpdata->inp.buf)
177 bcopy(to, from, inpdata->inp.len - (to - inpdata->inp.buf));
178 chng = to - from;
179 if (mv)
181 x -= chng;
182 inpdata->inp.pos -= chng;
184 inpdata->inp.len -= chng;
185 if (!(inpdata->inpmode & INP_NOECHO))
187 struct mchar mc;
188 char *s = from < to ? from : to;
189 mc = mchar_so;
190 while (s < inpdata->inp.buf+inpdata->inp.len)
192 mc.image = *s++;
193 LPutChar(flayer, &mc, x++, INPUTLINE);
195 while (chng--)
196 LPutChar(flayer, &mchar_blank, x++, INPUTLINE);
197 x = inpdata->inpstringlen + inpdata->inp.pos;
198 LGotoPos(flayer, x, INPUTLINE);
202 static void
203 InpProcess(ppbuf, plen)
204 char **ppbuf;
205 int *plen;
207 int len, x;
208 char *pbuf;
209 char ch;
210 struct inpdata *inpdata;
211 struct display *inpdisplay;
212 int prev, next;
214 inpdata = (struct inpdata *)flayer->l_data;
215 inpdisplay = display;
217 LGotoPos(flayer, inpdata->inpstringlen + (inpdata->inpmode & INP_NOECHO ? 0 : inpdata->inp.pos), INPUTLINE);
218 if (ppbuf == 0)
220 InpAbort();
221 return;
223 x = inpdata->inpstringlen + inpdata->inp.pos;
224 len = *plen;
225 pbuf = *ppbuf;
226 while (len)
228 char *p = inpdata->inp.buf + inpdata->inp.pos;
230 ch = *pbuf++;
231 len--;
232 if (inpdata->inpmode & INP_EVERY)
234 inpdata->inp.buf[inpdata->inp.len] = ch;
235 if (ch)
237 display = inpdisplay;
238 (*inpdata->inpfinfunc)(inpdata->inp.buf, inpdata->inp.len, inpdata->priv);
239 ch = inpdata->inp.buf[inpdata->inp.len];
242 else if (inpdata->inpmode & INP_RAW)
244 display = inpdisplay;
245 (*inpdata->inpfinfunc)(&ch, 1, inpdata->priv); /* raw */
246 if (ch)
247 continue;
249 if (((unsigned char)ch & 0177) >= ' ' && ch != 0177 && inpdata->inp.len < inpdata->inpmaxlen)
251 if (inpdata->inp.len > inpdata->inp.pos)
252 bcopy(p, p+1, inpdata->inp.len - inpdata->inp.pos);
253 inpdata->inp.buf[inpdata->inp.pos++] = ch;
254 inpdata->inp.len++;
256 if (!(inpdata->inpmode & INP_NOECHO))
258 struct mchar mc;
259 mc = mchar_so;
260 mc.image = *p++;
261 LPutChar(flayer, &mc, x, INPUTLINE);
262 x++;
263 if (p < inpdata->inp.buf+inpdata->inp.len)
265 while (p < inpdata->inp.buf+inpdata->inp.len)
267 mc.image = *p++;
268 LPutChar(flayer, &mc, x++, INPUTLINE);
270 x = inpdata->inpstringlen + inpdata->inp.pos;
271 LGotoPos(flayer, x, INPUTLINE);
275 else if ((ch == '\b' || ch == 0177) && inpdata->inp.pos > 0)
277 erase_chars(inpdata, p-1, p, x, 1);
279 else if (ch == '\025') /* CTRL-U */
281 x = inpdata->inpstringlen;
282 if (inpdata->inp.len && !(inpdata->inpmode & INP_NOECHO))
284 LClearArea(flayer, x, INPUTLINE, x + inpdata->inp.len - 1, INPUTLINE, 0, 0);
285 LGotoPos(flayer, x, INPUTLINE);
287 inpdata->inp.len = inpdata->inp.pos = 0;
289 else if (ch == '\013') /* CTRL-K */
291 x = inpdata->inpstringlen + inpdata->inp.pos;
292 if (inpdata->inp.len > inpdata->inp.pos && !(inpdata->inpmode & INP_NOECHO))
294 LClearArea(flayer, x, INPUTLINE, x + inpdata->inp.len - inpdata->inp.pos - 1, INPUTLINE, 0, 0);
295 LGotoPos(flayer, x, INPUTLINE);
297 inpdata->inp.len = inpdata->inp.pos;
299 else if (ch == '\027' && inpdata->inp.pos > 0) /* CTRL-W */
301 char *oldp = p--;
302 while (p > inpdata->inp.buf && *p == ' ')
303 p--;
304 while (p > inpdata->inp.buf && *(p - 1) != ' ')
305 p--;
306 erase_chars(inpdata, p, oldp, x, 1);
308 else if (ch == '\004' && inpdata->inp.pos < inpdata->inp.len) /* CTRL-D */
310 erase_chars(inpdata, p, p+1, x, 0);
312 else if (ch == '\001' || (unsigned char)ch == 0201) /* CTRL-A */
314 LGotoPos(flayer, x -= inpdata->inp.pos, INPUTLINE);
315 inpdata->inp.pos = 0;
317 else if ((ch == '\002' || (unsigned char)ch == 0202) && inpdata->inp.pos > 0) /* CTRL-B */
319 LGotoPos(flayer, --x, INPUTLINE);
320 inpdata->inp.pos--;
322 else if (ch == '\005' || (unsigned char)ch == 0205) /* CTRL-E */
324 LGotoPos(flayer, x += inpdata->inp.len - inpdata->inp.pos, INPUTLINE);
325 inpdata->inp.pos = inpdata->inp.len;
327 else if ((ch == '\006' || (unsigned char)ch == 0206) && inpdata->inp.pos < inpdata->inp.len) /* CTRL-F */
329 LGotoPos(flayer, ++x, INPUTLINE);
330 inpdata->inp.pos++;
332 else if ((prev = ((ch == '\020' || (unsigned char)ch == 0220) && /* CTRL-P */
333 inpdata->inp.prev)) ||
334 (next = ((ch == '\016' || (unsigned char)ch == 0216) && /* CTRL-N */
335 inpdata->inp.next)))
337 struct mchar mc;
338 mc = mchar_so;
339 if (inpdata->inp.len && !(inpdata->inpmode & INP_NOECHO))
340 LClearArea(flayer, inpdata->inpstringlen, INPUTLINE, inpdata->inpstringlen + inpdata->inp.len - 1, INPUTLINE, 0, 0);
342 if (prev && !inpdata->inp.next)
343 inphist = inpdata->inp;
344 memcpy(&inpdata->inp, prev ? inpdata->inp.prev : inpdata->inp.next, sizeof(struct inpline));
345 if (inpdata->inp.len > inpdata->inpmaxlen)
346 inpdata->inp.len = inpdata->inpmaxlen;
347 if (inpdata->inp.pos > inpdata->inp.len)
348 inpdata->inp.pos = inpdata->inp.len;
350 x = inpdata->inpstringlen;
351 p = inpdata->inp.buf;
353 if (!(inpdata->inpmode & INP_NOECHO))
355 while (p < inpdata->inp.buf+inpdata->inp.len)
357 mc.image = *p++;
358 LPutChar(flayer, &mc, x++, INPUTLINE);
361 x = inpdata->inpstringlen + inpdata->inp.pos;
362 LGotoPos(flayer, x, INPUTLINE);
365 else if (ch == '\003' || ch == '\007' || ch == '\033' ||
366 ch == '\000' || ch == '\n' || ch == '\r')
368 if (ch != '\n' && ch != '\r')
369 inpdata->inp.len = 0;
370 inpdata->inp.buf[inpdata->inp.len] = 0;
372 if (inpdata->inp.len && !(inpdata->inpmode & (INP_NOECHO | INP_RAW)))
374 struct inpline *store;
376 /* Look for a duplicate first */
377 for (store = inphist.prev; store; store = store->prev)
379 if (strcmp(store->buf, inpdata->inp.buf) == 0)
381 if (store->next)
382 store->next->prev = store->prev;
383 if (store->prev)
384 store->prev->next = store->next;
385 store->pos = inpdata->inp.pos;
386 break;
390 if (!store)
392 store = malloc(sizeof(struct inpline));
393 memcpy(store, &inpdata->inp, sizeof(struct inpline));
395 store->next = &inphist;
396 store->prev = inphist.prev;
397 if (inphist.prev)
398 inphist.prev->next = store;
399 inphist.prev = store;
402 flayer->l_data = 0; /* so inpdata does not get freed */
403 InpAbort(); /* redisplays... */
404 *ppbuf = pbuf;
405 *plen = len;
406 display = inpdisplay;
407 if ((inpdata->inpmode & INP_RAW) == 0)
408 (*inpdata->inpfinfunc)(inpdata->inp.buf, inpdata->inp.len, inpdata->priv);
409 else
410 (*inpdata->inpfinfunc)(pbuf - 1, 0, inpdata->priv);
411 free((char *)inpdata);
412 return;
415 if (!(inpdata->inpmode & INP_RAW))
417 flayer->l_x = inpdata->inpstringlen + (inpdata->inpmode & INP_NOECHO ? 0 : inpdata->inp.pos);
418 flayer->l_y = INPUTLINE;
420 *ppbuf = pbuf;
421 *plen = len;
424 static void
425 InpAbort()
427 LAY_CALL_UP(LayRedisplayLine(INPUTLINE, 0, flayer->l_width - 1, 0));
428 ExitOverlayPage();
431 static void
432 InpRedisplayLine(y, xs, xe, isblank)
433 int y, xs, xe, isblank;
435 int q, r, s, l, v;
436 struct inpdata *inpdata;
438 inpdata = (struct inpdata *)flayer->l_data;
439 if (y != INPUTLINE)
441 LAY_CALL_UP(LayRedisplayLine(y, xs, xe, isblank));
442 return;
444 inpdata->inp.buf[inpdata->inp.len] = 0;
445 q = xs;
446 v = xe - xs + 1;
447 s = 0;
448 r = inpdata->inpstringlen;
449 if (v > 0 && q < r)
451 l = v;
452 if (l > r - q)
453 l = r - q;
454 LPutStr(flayer, inpdata->inpstring + q - s, l, &mchar_so, q, y);
455 q += l;
456 v -= l;
458 s = r;
459 r += inpdata->inp.len;
460 if (!(inpdata->inpmode & INP_NOECHO) && v > 0 && q < r)
462 l = v;
463 if (l > r - q)
464 l = r - q;
465 LPutStr(flayer, inpdata->inp.buf + q - s, l, &mchar_so, q, y);
466 q += l;
467 v -= l;
469 s = r;
470 r = flayer->l_width;
471 if (!isblank && v > 0 && q < r)
473 l = v;
474 if (l > r - q)
475 l = r - q;
476 LClearArea(flayer, q, y, q + l - 1, y, 0, 0);
477 q += l;
482 InInput()
484 if (flayer && flayer->l_layfn == &InpLf)
485 return 1;
486 return 0;