get_wch.c revision 1.13 1 /* $NetBSD: get_wch.c,v 1.13 2017/01/30 14:55:58 roy Exp $ */
2
3 /*
4 * Copyright (c) 2005 The NetBSD Foundation Inc.
5 * All rights reserved.
6 *
7 * This code is derived from code donated to the NetBSD Foundation
8 * by Ruibiao Qiu <ruibiao (at) arl.wustl.edu,ruibiao (at) gmail.com>.
9 *
10 *
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.
22 *
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.
35 */
36
37 #include <sys/cdefs.h>
38 #ifndef lint
39 __RCSID("$NetBSD: get_wch.c,v 1.13 2017/01/30 14:55:58 roy Exp $");
40 #endif /* not lint */
41
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"
49
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 */
54
55 /* prototypes for private functions */
56 #ifdef HAVE_WCHAR
57 static int inkey(wchar_t *wc, int to, int delay);
58 #endif /* HAVE_WCHAR */
59
60 #ifdef HAVE_WCHAR
61 /*
62 * __init_get_wch - initialise all the pointers & structures needed to make
63 * get_wch work in keypad mode.
64 *
65 */
66 void
67 __init_get_wch(SCREEN *screen)
68 {
69 wstate = INKEY_NORM;
70 memset(&screen->cbuf, 0, sizeof(screen->cbuf));
71 screen->cbuf_head = screen->cbuf_tail = screen->cbuf_cur = 0;
72 }
73 #endif /* HAVE_WCHAR */
74
75
76 #ifdef HAVE_WCHAR
77 /*
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.
80 *
81 */
82 static int
83 inkey(wchar_t *wc, int to, int delay)
84 {
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 ];
94 int escdelay = _reentrant ?
95 _cursesi_screen->ESCDELAY : ESCDELAY;
96
97 #ifdef DEBUG
98 __CTRACE(__CTRACE_INPUT, "inkey (%p, %d, %d)\n", wc, to, delay);
99 #endif
100 for (;;) { /* loop until we get a complete key sequence */
101 if (wstate == INKEY_NORM) {
102 if (delay && __timeout(delay) == ERR)
103 return ERR;
104 c = fgetc(infd);
105 if (c == WEOF) {
106 clearerr(infd);
107 return ERR;
108 }
109
110 if (delay && (__notimeout() == ERR))
111 return ERR;
112
113 k = (wchar_t)c;
114 #ifdef DEBUG
115 __CTRACE(__CTRACE_INPUT,
116 "inkey (wstate normal) got '%s'\n", unctrl(k));
117 #endif
118
119 inbuf[*end] = k;
120 *end = (*end + 1) % MAX_CBUF_SIZE;
121 *working = *start;
122 wstate = INKEY_ASSEMBLING; /* go to assembling state */
123 #ifdef DEBUG
124 __CTRACE(__CTRACE_INPUT,
125 "inkey: NORM=>ASSEMBLING: start(%d), "
126 "current(%d), end(%d)\n", *start, *working, *end);
127 #endif /* DEBUG */
128 } else if (wstate == INKEY_BACKOUT) {
129 k = inbuf[*working];
130 *working = (*working + 1) % MAX_CBUF_SIZE;
131 if (*working == *end) { /* see if run out of keys */
132 /* if so, switch to assembling */
133 wstate = INKEY_ASSEMBLING;
134 #ifdef DEBUG
135 __CTRACE(__CTRACE_INPUT,
136 "inkey: BACKOUT=>ASSEMBLING, start(%d), "
137 "current(%d), end(%d)\n",
138 *start, *working, *end);
139 #endif /* DEBUG */
140 }
141 } else if (wstate == INKEY_ASSEMBLING) {
142 /* assembling a key sequence */
143 if (delay) {
144 if (__timeout(to ? (escdelay / 100) : delay)
145 == ERR)
146 return ERR;
147 } else {
148 if (to && (__timeout(escdelay / 100) == ERR))
149 return ERR;
150 }
151
152 c = fgetc(infd);
153 if (ferror(infd)) {
154 clearerr(infd);
155 return ERR;
156 }
157
158 if ((to || delay) && (__notimeout() == ERR))
159 return ERR;
160
161 k = (wchar_t)c;
162 #ifdef DEBUG
163 __CTRACE(__CTRACE_INPUT,
164 "inkey (wstate assembling) got '%s'\n", unctrl(k));
165 #endif /* DEBUG */
166 if (feof(infd)) { /* inter-char T/O, start backout */
167 clearerr(infd);
168 if (*start == *end)
169 /* no chars in the buffer, restart */
170 continue;
171
172 k = inbuf[*start];
173 wstate = INKEY_TIMEOUT;
174 #ifdef DEBUG
175 __CTRACE(__CTRACE_INPUT,
176 "inkey: ASSEMBLING=>TIMEOUT, start(%d), "
177 "current(%d), end(%d)\n",
178 *start, *working, *end);
179 #endif /* DEBUG */
180 } else {
181 inbuf[*end] = k;
182 *working = *end;
183 *end = (*end + 1) % MAX_CBUF_SIZE;
184 #ifdef DEBUG
185 __CTRACE(__CTRACE_INPUT,
186 "inkey: ASSEMBLING: start(%d), "
187 "current(%d), end(%d)",
188 *start, *working, *end);
189 #endif /* DEBUG */
190 }
191 } else if (wstate == INKEY_WCASSEMBLING) {
192 /* assembling a wide-char sequence */
193 if (delay) {
194 if (__timeout(to ? (escdelay / 100) : delay)
195 == ERR)
196 return ERR;
197 } else {
198 if (to && (__timeout(escdelay / 100) == ERR))
199 return ERR;
200 }
201
202 c = fgetc(infd);
203 if (ferror(infd)) {
204 clearerr(infd);
205 return ERR;
206 }
207
208 if ((to || delay) && (__notimeout() == ERR))
209 return ERR;
210
211 k = (wchar_t)c;
212 #ifdef DEBUG
213 __CTRACE(__CTRACE_INPUT,
214 "inkey (wstate wcassembling) got '%s'\n",
215 unctrl(k));
216 #endif
217 if (feof(infd)) { /* inter-char T/O, start backout */
218 clearerr(infd);
219 if (*start == *end)
220 /* no chars in the buffer, restart */
221 continue;
222
223 *wc = inbuf[*start];
224 *working = *start = (*start +1) % MAX_CBUF_SIZE;
225 if (*start == *end) {
226 state = wstate = INKEY_NORM;
227 #ifdef DEBUG
228 __CTRACE(__CTRACE_INPUT,
229 "inkey: WCASSEMBLING=>NORM, "
230 "start(%d), current(%d), end(%d)",
231 *start, *working, *end);
232 #endif /* DEBUG */
233 } else {
234 state = wstate = INKEY_BACKOUT;
235 #ifdef DEBUG
236 __CTRACE(__CTRACE_INPUT,
237 "inkey: WCASSEMBLING=>BACKOUT, "
238 "start(%d), current(%d), end(%d)",
239 *start, *working, *end);
240 #endif /* DEBUG */
241 }
242 return OK;
243 } else {
244 /* assembling wide characters */
245 inbuf[*end] = k;
246 *working = *end;
247 *end = (*end + 1) % MAX_CBUF_SIZE;
248 #ifdef DEBUG
249 __CTRACE(__CTRACE_INPUT,
250 "inkey: WCASSEMBLING[head(%d), "
251 "urrent(%d), tail(%d)]\n",
252 *start, *working, *end);
253 #endif /* DEBUG */
254 ret = (int)mbrtowc(wc, inbuf + (*working), 1,
255 &_cursesi_screen->sp);
256 #ifdef DEBUG
257 __CTRACE(__CTRACE_INPUT,
258 "inkey: mbrtowc returns %d, wc(%x)\n",
259 ret, *wc);
260 #endif /* DEBUG */
261 if (ret == -2) {
262 *working = (*working+1) % MAX_CBUF_SIZE;
263 continue;
264 }
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 */
289 }
290
291 if (*start == *end) {
292 /* only one char processed */
293 state = wstate = INKEY_NORM;
294 #ifdef DEBUG
295 __CTRACE(__CTRACE_INPUT,
296 "inkey: WCASSEMBLING=>NORM, "
297 "start(%d), current(%d), end(%d)",
298 *start, *working, *end);
299 #endif /* DEBUG */
300 } else {
301 /* otherwise we must have more than
302 * one char to backout */
303 state = wstate = INKEY_BACKOUT;
304 #ifdef DEBUG
305 __CTRACE(__CTRACE_INPUT,
306 "inkey: WCASSEMBLING=>BACKOUT, "
307 "start(%d), current(%d), end(%d)",
308 *start, *working, *end);
309 #endif /* DEBUG */
310 }
311 return OK;
312 }
313 } else {
314 fprintf(stderr, "Inkey wstate screwed - exiting!!!");
315 exit(2);
316 }
317
318 /*
319 * Check key has no special meaning and we have not
320 * timed out and the key has not been disabled
321 */
322 mapping = current->mapping[k];
323 if (((wstate == INKEY_TIMEOUT) || (mapping < 0))
324 || ((current->key[mapping]->type
325 == KEYMAP_LEAF)
326 && (current->key[mapping]->enable == FALSE)))
327 {
328 /* wide-character specific code */
329 #ifdef DEBUG
330 __CTRACE(__CTRACE_INPUT,
331 "inkey: Checking for wide char\n");
332 #endif /* DEBUG */
333 mbrtowc( NULL, NULL, 1, &_cursesi_screen->sp );
334 *working = *start;
335 mlen = *end > *working ?
336 *end - *working : MAX_CBUF_SIZE - *working;
337 if (!mlen)
338 return ERR;
339 #ifdef DEBUG
340 __CTRACE(__CTRACE_INPUT,
341 "inkey: Check wide char[head(%d), "
342 "current(%d), tail(%d), mlen(%ld)]\n",
343 *start, *working, *end, (long) mlen);
344 #endif /* DEBUG */
345 ret = (int)mbrtowc(wc, inbuf + (*working), mlen,
346 &_cursesi_screen->sp);
347 #ifdef DEBUG
348 __CTRACE(__CTRACE_INPUT,
349 "inkey: mbrtowc returns %d, wc(%x)\n", ret, *wc);
350 #endif /* DEBUG */
351 if (ret == -2 && *end < *working) {
352 /* second half of a wide character */
353 *working = 0;
354 mlen = *end;
355 if (mlen)
356 ret = (int)mbrtowc(wc, inbuf, mlen,
357 &_cursesi_screen->sp);
358 }
359 if (ret == -2 && wstate != INKEY_TIMEOUT) {
360 *working = (*working + (int) mlen)
361 % MAX_CBUF_SIZE;
362 wstate = INKEY_WCASSEMBLING;
363 continue;
364 }
365 if (ret == 0)
366 ret = 1;
367 if (ret == -1) {
368 /* return the first key we know about */
369 *wc = inbuf[*start];
370 *working = *start
371 = (*start + 1) % MAX_CBUF_SIZE;
372 #ifdef DEBUG
373 __CTRACE(__CTRACE_INPUT,
374 "inkey: Invalid wide char(%x)[head(%d), "
375 "current(%d), tail(%d)]\n",
376 *wc, *start, *working, *end);
377 #endif /* DEBUG */
378 } else { /* > 0 */
379 /* return the wide character */
380 *start = *working
381 = (*working + ret) % MAX_CBUF_SIZE;
382 #ifdef DEBUG
383 __CTRACE(__CTRACE_INPUT,
384 "inkey: Wide char found(%x)[head(%d), "
385 "current(%d), tail(%d)]\n",
386 *wc, *start, *working, *end);
387 #endif /* DEBUG */
388 }
389
390 if (*start == *end) { /* only one char processed */
391 state = wstate = INKEY_NORM;
392 #ifdef DEBUG
393 __CTRACE(__CTRACE_INPUT,
394 "inkey: Empty cbuf=>NORM, "
395 "start(%d), current(%d), end(%d)\n",
396 *start, *working, *end);
397 #endif /* DEBUG */
398 } else {
399 /* otherwise we must have more than one
400 * char to backout */
401 state = wstate = INKEY_BACKOUT;
402 #ifdef DEBUG
403 __CTRACE(__CTRACE_INPUT,
404 "inkey: Non-empty cbuf=>BACKOUT, "
405 "start(%d), current(%d), end(%d)\n",
406 *start, *working, *end);
407 #endif /* DEBUG */
408 }
409 return OK;
410 } else { /* must be part of a multikey sequence */
411 /* check for completed key sequence */
412 if (current->key[current->mapping[k]]->type
413 == KEYMAP_LEAF) {
414 /* eat the key sequence in cbuf */
415 *start = *working = ( *working + 1 )
416 % MAX_CBUF_SIZE;
417
418 /* check if inbuf empty now */
419 #ifdef DEBUG
420 __CTRACE(__CTRACE_INPUT,
421 "inkey: Key found(%s)\n",
422 key_name(current->key[mapping]->value.symbol));
423 #endif /* DEBUG */
424 if (*start == *end) {
425 /* if it is go back to normal */
426 state = wstate = INKEY_NORM;
427 #ifdef DEBUG
428 __CTRACE(__CTRACE_INPUT,
429 "[inkey]=>NORM, start(%d), "
430 "current(%d), end(%d)",
431 *start, *working, *end);
432 #endif /* DEBUG */
433 } else {
434 /* otherwise go to backout state */
435 state = wstate = INKEY_BACKOUT;
436 #ifdef DEBUG
437 __CTRACE(__CTRACE_INPUT,
438 "[inkey]=>BACKOUT, start(%d), "
439 "current(%d), end(%d)",
440 *start, *working, *end );
441 #endif /* DEBUG */
442 }
443
444 /* return the symbol */
445 *wc = current->key[mapping]->value.symbol;
446 return KEY_CODE_YES;
447 } else {
448 /* Step to next part of multi-key sequence */
449 current = current->key[current->mapping[k]]->value.next;
450 }
451 }
452 }
453 }
454 #endif /* HAVE_WCHAR */
455
456 /*
457 * get_wch --
458 * Read in a wide character from stdscr.
459 */
460 int
461 get_wch(wint_t *ch)
462 {
463 #ifndef HAVE_WCHAR
464 return ERR;
465 #else
466 return wget_wch(stdscr, ch);
467 #endif /* HAVE_WCHAR */
468 }
469
470 /*
471 * mvget_wch --
472 * Read in a character from stdscr at the given location.
473 */
474 int
475 mvget_wch(int y, int x, wint_t *ch)
476 {
477 #ifndef HAVE_WCHAR
478 return ERR;
479 #else
480 return mvwget_wch(stdscr, y, x, ch);
481 #endif /* HAVE_WCHAR */
482 }
483
484 /*
485 * mvwget_wch --
486 * Read in a character from stdscr at the given location in the
487 * given window.
488 */
489 int
490 mvwget_wch(WINDOW *win, int y, int x, wint_t *ch)
491 {
492 #ifndef HAVE_WCHAR
493 return ERR;
494 #else
495 if (wmove(win, y, x) == ERR)
496 return ERR;
497
498 return wget_wch(win, ch);
499 #endif /* HAVE_WCHAR */
500 }
501
502 /*
503 * wget_wch --
504 * Read in a wide character from the window.
505 */
506 int
507 wget_wch(WINDOW *win, wint_t *ch)
508 {
509 #ifndef HAVE_WCHAR
510 return ERR;
511 #else
512 int ret, weset;
513 int c;
514 FILE *infd = _cursesi_screen->infd;
515 cchar_t wc;
516 wchar_t inp, ws[2];
517
518 if (!(win->flags & __SCROLLOK)
519 && (win->flags & __FULLWIN)
520 && win->curx == win->maxx - 1
521 && win->cury == win->maxy - 1
522 && __echoit)
523 return ERR;
524
525 if (is_wintouched(win))
526 wrefresh(win);
527 #ifdef DEBUG
528 __CTRACE(__CTRACE_INPUT, "wget_wch: __echoit = %d, "
529 "__rawmode = %d, __nl = %d, flags = %#.4x\n",
530 __echoit, __rawmode, _cursesi_screen->nl, win->flags);
531 #endif
532 if (_cursesi_screen->resized) {
533 _cursesi_screen->resized = 0;
534 *ch = KEY_RESIZE;
535 return KEY_CODE_YES;
536 }
537 if (_cursesi_screen->unget_pos) {
538 #ifdef DEBUG
539 __CTRACE(__CTRACE_INPUT, "wget_wch returning char at %d\n",
540 _cursesi_screen->unget_pos);
541 #endif
542 _cursesi_screen->unget_pos--;
543 *ch = _cursesi_screen->unget_list[_cursesi_screen->unget_pos];
544 if (__echoit) {
545 ws[0] = *ch, ws[1] = L'\0';
546 setcchar(&wc, ws, win->wattr, 0, NULL);
547 wadd_wch(win, &wc);
548 }
549 return KEY_CODE_YES;
550 }
551 if (__echoit && !__rawmode) {
552 cbreak();
553 weset = 1;
554 } else
555 weset = 0;
556
557 __save_termios();
558
559 if (win->flags & __KEYPAD) {
560 switch (win->delay) {
561 case -1:
562 ret = inkey(&inp,
563 win->flags & __NOTIMEOUT ? 0 : 1, 0);
564 break;
565 case 0:
566 if (__nodelay() == ERR)
567 return ERR;
568 ret = inkey(&inp, 0, 0);
569 break;
570 default:
571 ret = inkey(&inp,
572 win->flags & __NOTIMEOUT ? 0 : 1,
573 win->delay);
574 break;
575 }
576 if ( ret == ERR )
577 return ERR;
578 } else {
579 switch (win->delay) {
580 case -1:
581 break;
582 case 0:
583 if (__nodelay() == ERR)
584 return ERR;
585 break;
586 default:
587 if (__timeout(win->delay) == ERR)
588 return ERR;
589 break;
590 }
591
592 c = getwchar();
593 if (feof(infd)) {
594 clearerr(infd);
595 __restore_termios();
596 return ERR; /* we have timed out */
597 }
598
599 if (ferror(infd)) {
600 clearerr(infd);
601 return ERR;
602 } else {
603 ret = c;
604 inp = c;
605 }
606 }
607 #ifdef DEBUG
608 if (inp > 255)
609 /* we have a key symbol - treat it differently */
610 /* XXXX perhaps __unctrl should be expanded to include
611 * XXXX the keysyms in the table....
612 */
613 __CTRACE(__CTRACE_INPUT, "wget_wch assembled keysym 0x%x\n",
614 inp);
615 else
616 __CTRACE(__CTRACE_INPUT, "wget_wch got '%s'\n", unctrl(inp));
617 #endif
618 if (win->delay > -1) {
619 if (__delay() == ERR)
620 return ERR;
621 }
622
623 __restore_termios();
624
625 if (__echoit) {
626 if ( ret == KEY_CODE_YES ) {
627 /* handle [DEL], [BS], and [LEFT] */
628 if ( win->curx &&
629 ( inp == KEY_DC ||
630 inp == KEY_BACKSPACE ||
631 inp == KEY_LEFT )) {
632 wmove( win, win->cury, win->curx - 1 );
633 wdelch( win );
634 }
635 } else {
636 ws[ 0 ] = inp, ws[ 1 ] = L'\0';
637 setcchar( &wc, ws, win->wattr, 0, NULL );
638 wadd_wch( win, &wc );
639 }
640 }
641
642 if (weset)
643 nocbreak();
644
645 if (_cursesi_screen->nl && inp == 13)
646 inp = 10;
647
648 *ch = inp;
649
650 if ( ret == KEY_CODE_YES )
651 return KEY_CODE_YES;
652 return inp < 0 ? ERR : OK;
653 #endif /* HAVE_WCHAR */
654 }
655
656 /*
657 * unget_wch --
658 * Put the wide character back into the input queue.
659 */
660 int
661 unget_wch(const wchar_t c)
662 {
663 return __unget((wint_t)c);
664 }
665