worldstone: add -s for statistical profiling
[minix.git] / lib / libcurses / get_wch.c
blob4430467d043e0290cf690d3fc44e8c3a39ec2a90
1 /* $NetBSD: get_wch.c,v 1.9 2010/12/16 17:42:28 wiz Exp $ */
3 /*
4 * Copyright (c) 2005 The NetBSD Foundation Inc.
5 * All rights reserved.
7 * This code is derived from code donated to the NetBSD Foundation
8 * by Ruibiao Qiu <ruibiao@arl.wustl.edu,ruibiao@gmail.com>.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the NetBSD Foundation nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
24 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
37 #include <sys/cdefs.h>
38 #ifndef lint
39 __RCSID("$NetBSD: get_wch.c,v 1.9 2010/12/16 17:42:28 wiz Exp $");
40 #endif /* not lint */
42 #include <string.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45 #include <stdio.h>
46 #include "curses.h"
47 #include "curses_private.h"
48 #include "keymap.h"
50 #ifdef HAVE_WCHAR
51 static short wstate; /* state of the wcinkey function */
52 #endif /* HAVE_WCHAR */
53 extern short state; /* storage declared in getch.c */
55 /* prototypes for private functions */
56 #ifdef HAVE_WCHAR
57 static int inkey(wchar_t *wc, int to, int delay);
58 #endif /* HAVE_WCHAR */
60 #ifdef HAVE_WCHAR
62 * __init_get_wch - initialise all the pointers & structures needed to make
63 * get_wch work in keypad mode.
66 void
67 __init_get_wch(SCREEN *screen)
69 wstate = INKEY_NORM;
70 memset( &screen->cbuf, 0, MAX_CBUF_SIZE * sizeof( int ));
71 screen->cbuf_head = screen->cbuf_tail = screen->cbuf_cur = 0;
73 #endif /* HAVE_WCHAR */
76 #ifdef HAVE_WCHAR
78 * inkey - do the work to process keyboard input, check for multi-key
79 * sequences and return the appropriate symbol if we get a match.
82 static int
83 inkey(wchar_t *wc, int to, int delay)
85 wchar_t k = 0;
86 int c, mapping, ret = 0;
87 size_t mlen = 0;
88 keymap_t *current = _cursesi_screen->base_keymap;
89 FILE *infd = _cursesi_screen->infd;
90 int *start = &_cursesi_screen->cbuf_head,
91 *working = &_cursesi_screen->cbuf_cur,
92 *end = &_cursesi_screen->cbuf_tail;
93 char *inbuf = &_cursesi_screen->cbuf[ 0 ];
95 #ifdef DEBUG
96 __CTRACE(__CTRACE_INPUT, "inkey (%p, %d, %d)\n", wc, to, delay);
97 #endif
98 for (;;) { /* loop until we get a complete key sequence */
99 if (wstate == INKEY_NORM) {
100 if (delay && __timeout(delay) == ERR)
101 return ERR;
102 c = fgetc(infd);
103 if (c == WEOF) {
104 clearerr(infd);
105 return ERR;
108 if (delay && (__notimeout() == ERR))
109 return ERR;
111 k = (wchar_t) c;
112 #ifdef DEBUG
113 __CTRACE(__CTRACE_INPUT,
114 "inkey (wstate normal) got '%s'\n", unctrl(k));
115 #endif
117 inbuf[ *end ] = k;
118 *end = ( *end + 1 ) % MAX_CBUF_SIZE;
119 *working = *start;
120 wstate = INKEY_ASSEMBLING; /* go to assembling state */
121 #ifdef DEBUG
122 __CTRACE(__CTRACE_INPUT,
123 "inkey: NORM=>ASSEMBLING: start(%d), "
124 "current(%d), end(%d)\n", *start, *working, *end);
125 #endif /* DEBUG */
126 } else if (wstate == INKEY_BACKOUT) {
127 k = inbuf[*working];
128 *working = ( *working + 1 ) % MAX_CBUF_SIZE;
129 if (*working == *end) { /* see if run out of keys */
130 /* if so, switch to assembling */
131 wstate = INKEY_ASSEMBLING;
132 #ifdef DEBUG
133 __CTRACE(__CTRACE_INPUT,
134 "inkey: BACKOUT=>ASSEMBLING, start(%d), "
135 "current(%d), end(%d)\n",
136 *start, *working, *end);
137 #endif /* DEBUG */
139 } else if (wstate == INKEY_ASSEMBLING) {
140 /* assembling a key sequence */
141 if (delay) {
142 if (__timeout(to ? (ESCDELAY / 100) : delay)
143 == ERR)
144 return ERR;
145 } else {
146 if (to && (__timeout(ESCDELAY / 100) == ERR))
147 return ERR;
150 c = fgetc(infd);
151 if (ferror(infd)) {
152 clearerr(infd);
153 return ERR;
156 if ((to || delay) && (__notimeout() == ERR))
157 return ERR;
159 k = (wchar_t) c;
160 #ifdef DEBUG
161 __CTRACE(__CTRACE_INPUT,
162 "inkey (wstate assembling) got '%s'\n", unctrl(k));
163 #endif /* DEBUG */
164 if (feof(infd)) { /* inter-char T/O, start backout */
165 clearerr(infd);
166 if (*start == *end)
167 /* no chars in the buffer, restart */
168 continue;
170 k = inbuf[*start];
171 wstate = INKEY_TIMEOUT;
172 #ifdef DEBUG
173 __CTRACE(__CTRACE_INPUT,
174 "inkey: ASSEMBLING=>TIMEOUT, start(%d), "
175 "current(%d), end(%d)\n",
176 *start, *working, *end);
177 #endif /* DEBUG */
178 } else {
179 inbuf[ *end ] = k;
180 *working = *end;
181 *end = ( *end + 1 ) % MAX_CBUF_SIZE;
182 #ifdef DEBUG
183 __CTRACE(__CTRACE_INPUT,
184 "inkey: ASSEMBLING: start(%d), "
185 "current(%d), end(%d)",
186 *start, *working, *end);
187 #endif /* DEBUG */
189 } else if (wstate == INKEY_WCASSEMBLING) {
190 /* assembling a wide-char sequence */
191 if (delay) {
192 if (__timeout(to ? (ESCDELAY / 100) : delay)
193 == ERR)
194 return ERR;
195 } else {
196 if (to && (__timeout(ESCDELAY / 100) == ERR))
197 return ERR;
200 c = fgetc(infd);
201 if (ferror(infd)) {
202 clearerr(infd);
203 return ERR;
206 if ((to || delay) && (__notimeout() == ERR))
207 return ERR;
209 k = (wchar_t) c;
210 #ifdef DEBUG
211 __CTRACE(__CTRACE_INPUT,
212 "inkey (wstate wcassembling) got '%s'\n",
213 unctrl(k));
214 #endif
215 if (feof(infd)) { /* inter-char T/O, start backout */
216 clearerr(infd);
217 if (*start == *end)
218 /* no chars in the buffer, restart */
219 continue;
221 *wc = inbuf[*start];
222 *working = *start
223 = ( *start + 1 ) % MAX_CBUF_SIZE;
224 if (*start == *end) {
225 state = wstate = INKEY_NORM;
226 #ifdef DEBUG
227 __CTRACE(__CTRACE_INPUT,
228 "inkey: WCASSEMBLING=>NORM, "
229 "start(%d), current(%d), end(%d)",
230 *start, *working, *end);
231 #endif /* DEBUG */
232 } else {
233 state = wstate = INKEY_BACKOUT;
234 #ifdef DEBUG
235 __CTRACE(__CTRACE_INPUT,
236 "inkey: WCASSEMBLING=>BACKOUT, "
237 "start(%d), current(%d), end(%d)",
238 *start, *working, *end);
239 #endif /* DEBUG */
241 return OK;
242 } else {
243 /* assembling wide characters */
244 inbuf[ *end ] = k;
245 *working = *end;
246 *end = ( *end + 1 ) % MAX_CBUF_SIZE;
247 #ifdef DEBUG
248 __CTRACE(__CTRACE_INPUT,
249 "inkey: WCASSEMBLING[head(%d), "
250 "urrent(%d), tail(%d)]\n",
251 *start, *working, *end);
252 #endif /* DEBUG */
253 ret = (int) mbrtowc( wc, inbuf + (*working), 1,
254 &_cursesi_screen->sp );
255 #ifdef DEBUG
256 __CTRACE(__CTRACE_INPUT,
257 "inkey: mbrtowc returns %d, wc(%x)\n",
258 ret, *wc );
259 #endif /* DEBUG */
260 if ( ret == -2 ) {
261 *working = (*working + 1)
262 % MAX_CBUF_SIZE;
263 continue;
265 if ( ret == 0 )
266 ret = 1;
267 if ( ret == -1 ) {
268 /* return the 1st character we know */
269 *wc = inbuf[ *start ];
270 *working = *start = ( *start + 1 ) % MAX_CBUF_SIZE;
271 #ifdef DEBUG
272 __CTRACE(__CTRACE_INPUT,
273 "inkey: Invalid wide char(%x) "
274 "[head(%d), current(%d), "
275 "tail(%d)]\n",
276 *wc, *start, *working, *end);
277 #endif /* DEBUG */
278 } else { /* > 0 */
279 /* return the wide character */
280 *start = *working
281 = (*working + ret)%MAX_CBUF_SIZE;
282 #ifdef DEBUG
283 __CTRACE(__CTRACE_INPUT,
284 "inkey: Wide char found(%x) "
285 "[head(%d), current(%d), "
286 "tail(%d)]\n",
287 *wc, *start, *working, *end);
288 #endif /* DEBUG */
291 if (*start == *end) { /* only one char processed */
292 state = wstate = INKEY_NORM;
293 #ifdef DEBUG
294 __CTRACE(__CTRACE_INPUT,
295 "inkey: WCASSEMBLING=>NORM, "
296 "start(%d), current(%d), end(%d)",
297 *start, *working, *end);
298 #endif /* DEBUG */
299 } else {
300 /* otherwise we must have more than one char to backout */
301 state = wstate = INKEY_BACKOUT;
302 #ifdef DEBUG
303 __CTRACE(__CTRACE_INPUT,
304 "inkey: WCASSEMBLING=>BACKOUT, "
305 "start(%d), current(%d), end(%d)",
306 *start, *working, *end);
307 #endif /* DEBUG */
309 return OK;
311 } else {
312 fprintf(stderr, "Inkey wstate screwed - exiting!!!");
313 exit(2);
317 * Check key has no special meaning and we have not
318 * timed out and the key has not been disabled
320 mapping = current->mapping[k];
321 if (((wstate == INKEY_TIMEOUT) || (mapping < 0))
322 || ((current->key[mapping]->type
323 == KEYMAP_LEAF)
324 && (current->key[mapping]->enable == FALSE))) {
325 /* wide-character specific code */
326 #ifdef DEBUG
327 __CTRACE(__CTRACE_INPUT,
328 "inkey: Checking for wide char\n");
329 #endif /* DEBUG */
330 mbrtowc( NULL, NULL, 1, &_cursesi_screen->sp );
331 *working = *start;
332 mlen = *end > *working ?
333 *end - *working : MAX_CBUF_SIZE - *working;
334 if ( !mlen )
335 return ERR;
336 #ifdef DEBUG
337 __CTRACE(__CTRACE_INPUT,
338 "inkey: Check wide char[head(%d), "
339 "current(%d), tail(%d), mlen(%ld)]\n",
340 *start, *working, *end, (long) mlen);
341 #endif /* DEBUG */
342 ret = (int) mbrtowc( wc, inbuf + (*working), mlen,
343 &_cursesi_screen->sp );
344 #ifdef DEBUG
345 __CTRACE(__CTRACE_INPUT,
346 "inkey: mbrtowc returns %d, wc(%x)\n", ret, *wc);
347 #endif /* DEBUG */
348 if ( ret == -2 && *end < *working ) {
349 /* second half of a wide character */
350 *working = 0;
351 mlen = *end;
352 if ( mlen )
353 ret = (int) mbrtowc( wc, inbuf, mlen,
354 &_cursesi_screen->sp );
356 if ( ret == -2 && wstate != INKEY_TIMEOUT ) {
357 *working = (*working + (int) mlen)
358 % MAX_CBUF_SIZE;
359 wstate = INKEY_WCASSEMBLING;
360 continue;
362 if ( ret == 0 )
363 ret = 1;
364 if ( ret == -1 ) {
365 /* return the first key we know about */
366 *wc = inbuf[ *start ];
367 *working = *start
368 = ( *start + 1 ) % MAX_CBUF_SIZE;
369 #ifdef DEBUG
370 __CTRACE(__CTRACE_INPUT,
371 "inkey: Invalid wide char(%x)[head(%d), "
372 "current(%d), tail(%d)]\n",
373 *wc, *start, *working, *end);
374 #endif /* DEBUG */
375 } else { /* > 0 */
376 /* return the wide character */
377 *start = *working
378 = ( *working + ret ) % MAX_CBUF_SIZE;
379 #ifdef DEBUG
380 __CTRACE(__CTRACE_INPUT,
381 "inkey: Wide char found(%x)[head(%d), "
382 "current(%d), tail(%d)]\n",
383 *wc, *start, *working, *end);
384 #endif /* DEBUG */
387 if (*start == *end) { /* only one char processed */
388 state = wstate = INKEY_NORM;
389 #ifdef DEBUG
390 __CTRACE(__CTRACE_INPUT,
391 "inkey: Empty cbuf=>NORM, "
392 "start(%d), current(%d), end(%d)\n",
393 *start, *working, *end);
394 #endif /* DEBUG */
395 } else {
396 /* otherwise we must have more than one char to backout */
397 state = wstate = INKEY_BACKOUT;
398 #ifdef DEBUG
399 __CTRACE(__CTRACE_INPUT,
400 "inkey: Non-empty cbuf=>BACKOUT, "
401 "start(%d), current(%d), end(%d)\n",
402 *start, *working, *end);
403 #endif /* DEBUG */
405 return OK;
406 } else { /* must be part of a multikey sequence */
407 /* check for completed key sequence */
408 if (current->key[current->mapping[k]]->type
409 == KEYMAP_LEAF) {
410 /* eat the key sequence in cbuf */
411 *start = *working = ( *working + 1 ) % MAX_CBUF_SIZE;
413 /* check if inbuf empty now */
414 #ifdef DEBUG
415 __CTRACE(__CTRACE_INPUT,
416 "inkey: Key found(%s)\n",
417 key_name(current->key[mapping]->value.symbol));
418 #endif /* DEBUG */
419 if (*start == *end) {
420 /* if it is go back to normal */
421 state = wstate = INKEY_NORM;
422 #ifdef DEBUG
423 __CTRACE(__CTRACE_INPUT,
424 "[inkey]=>NORM, start(%d), "
425 "current(%d), end(%d)",
426 *start, *working, *end);
427 #endif /* DEBUG */
428 } else {
429 /* otherwise go to backout state */
430 state = wstate = INKEY_BACKOUT;
431 #ifdef DEBUG
432 __CTRACE(__CTRACE_INPUT,
433 "[inkey]=>BACKOUT, start(%d), "
434 "current(%d), end(%d)",
435 *start, *working, *end );
436 #endif /* DEBUG */
439 /* return the symbol */
440 *wc = current->key[mapping]->value.symbol;
441 return KEY_CODE_YES;
442 } else {
443 /* Step to next part of multi-key sequence */
444 current = current->key[current->mapping[k]]->value.next;
449 #endif /* HAVE_WCHAR */
452 * get_wch --
453 * Read in a wide character from stdscr.
456 get_wch(wint_t *ch)
458 #ifndef HAVE_WCHAR
459 return ERR;
460 #else
461 return wget_wch(stdscr, ch);
462 #endif /* HAVE_WCHAR */
466 * mvget_wch --
467 * Read in a character from stdscr at the given location.
470 mvget_wch(int y, int x, wint_t *ch)
472 #ifndef HAVE_WCHAR
473 return ERR;
474 #else
475 return mvwget_wch(stdscr, y, x, ch);
476 #endif /* HAVE_WCHAR */
480 * mvwget_wch --
481 * Read in a character from stdscr at the given location in the
482 * given window.
485 mvwget_wch(WINDOW *win, int y, int x, wint_t *ch)
487 #ifndef HAVE_WCHAR
488 return ERR;
489 #else
490 if (wmove(win, y, x) == ERR)
491 return ERR;
493 return wget_wch(win, ch);
494 #endif /* HAVE_WCHAR */
498 * wget_wch --
499 * Read in a wide character from the window.
502 wget_wch(WINDOW *win, wint_t *ch)
504 #ifndef HAVE_WCHAR
505 return ERR;
506 #else
507 int ret, weset;
508 int c;
509 FILE *infd = _cursesi_screen->infd;
510 cchar_t wc;
511 wchar_t inp, ws[ 2 ];
513 if (!(win->flags & __SCROLLOK) && (win->flags & __FULLWIN)
514 && win->curx == win->maxx - 1
515 && win->cury == win->maxy - 1
516 && __echoit)
517 return (ERR);
519 if (is_wintouched(win))
520 wrefresh(win);
521 #ifdef DEBUG
522 __CTRACE(__CTRACE_INPUT, "wget_wch: __echoit = %d, "
523 "__rawmode = %d, __nl = %d, flags = %#.4x\n",
524 __echoit, __rawmode, _cursesi_screen->nl, win->flags);
525 #endif
526 if (_cursesi_screen->resized) {
527 _cursesi_screen->resized = 0;
528 *ch = KEY_RESIZE;
529 return KEY_CODE_YES;
531 if (_cursesi_screen->unget_pos) {
532 #ifdef DEBUG
533 __CTRACE(__CTRACE_INPUT, "wget_wch returning char at %d\n",
534 _cursesi_screen->unget_pos);
535 #endif
536 _cursesi_screen->unget_pos--;
537 *ch = _cursesi_screen->unget_list[_cursesi_screen->unget_pos];
538 if (__echoit) {
539 ws[0] = *ch, ws[1] = L'\0';
540 setcchar(&wc, ws, win->wattr, 0, NULL);
541 wadd_wch(win, &wc);
543 return KEY_CODE_YES;
545 if (__echoit && !__rawmode) {
546 cbreak();
547 weset = 1;
548 } else
549 weset = 0;
551 __save_termios();
553 if (win->flags & __KEYPAD) {
554 switch (win->delay) {
555 case -1:
556 ret = inkey(&inp,
557 win->flags & __NOTIMEOUT ? 0 : 1, 0);
558 break;
559 case 0:
560 if (__nodelay() == ERR)
561 return ERR;
562 ret = inkey(&inp, 0, 0);
563 break;
564 default:
565 ret = inkey(&inp,
566 win->flags & __NOTIMEOUT ? 0 : 1,
567 win->delay);
568 break;
570 if ( ret == ERR )
571 return ERR;
572 } else {
573 switch (win->delay) {
574 case -1:
575 break;
576 case 0:
577 if (__nodelay() == ERR)
578 return ERR;
579 break;
580 default:
581 if (__timeout(win->delay) == ERR)
582 return ERR;
583 break;
586 c = getwchar();
587 if (feof(infd)) {
588 clearerr(infd);
589 __restore_termios();
590 return ERR; /* we have timed out */
593 if (ferror(infd)) {
594 clearerr(infd);
595 return ERR;
596 } else {
597 ret = c;
598 inp = c;
601 #ifdef DEBUG
602 if (inp > 255)
603 /* we have a key symbol - treat it differently */
604 /* XXXX perhaps __unctrl should be expanded to include
605 * XXXX the keysyms in the table....
607 __CTRACE(__CTRACE_INPUT, "wget_wch assembled keysym 0x%x\n",
608 inp);
609 else
610 __CTRACE(__CTRACE_INPUT, "wget_wch got '%s'\n", unctrl(inp));
611 #endif
612 if (win->delay > -1) {
613 if (__delay() == ERR)
614 return ERR;
617 __restore_termios();
619 if (__echoit) {
620 if ( ret == KEY_CODE_YES ) {
621 /* handle [DEL], [BS], and [LEFT] */
622 if ( win->curx &&
623 ( inp == KEY_DC ||
624 inp == KEY_BACKSPACE ||
625 inp == KEY_LEFT )) {
626 wmove( win, win->cury, win->curx - 1 );
627 wdelch( win );
629 } else {
630 ws[ 0 ] = inp, ws[ 1 ] = L'\0';
631 setcchar( &wc, ws, win->wattr, 0, NULL );
632 wadd_wch( win, &wc );
636 if (weset)
637 nocbreak();
639 if (_cursesi_screen->nl && inp == 13)
640 inp = 10;
642 *ch = inp;
644 if ( ret == KEY_CODE_YES )
645 return KEY_CODE_YES;
646 return ( inp < 0 ? ERR : OK );
647 #endif /* HAVE_WCHAR */
651 * unget_wch --
652 * Put the wide character back into the input queue.
655 unget_wch(const wchar_t c)
657 return __unget((wint_t) c);