get_wch.c revision 1.14 1 /* $NetBSD: get_wch.c,v 1.14 2017/01/31 09:17:53 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.14 2017/01/31 09:17:53 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
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;
106 }
107
108 if (delay && (__notimeout() == ERR))
109 return ERR;
110
111 k = (wchar_t)c;
112 #ifdef DEBUG
113 __CTRACE(__CTRACE_INPUT,
114 "inkey (wstate normal) got '%s'\n", unctrl(k));
115 #endif
116
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 */
138 }
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;
148 }
149
150 c = fgetc(infd);
151 if (ferror(infd)) {
152 clearerr(infd);
153 return ERR;
154 }
155
156 if ((to || delay) && (__notimeout() == ERR))
157 return ERR;
158
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;
169
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 */
188 }
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;
198 }
199
200 c = fgetc(infd);
201 if (ferror(infd)) {
202 clearerr(infd);
203 return ERR;
204 }
205
206 if ((to || delay) && (__notimeout() == ERR))
207 return ERR;
208
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;
220
221 *wc = inbuf[*start];
222 *working = *start = (*start +1) % MAX_CBUF_SIZE;
223 if (*start == *end) {
224 state = wstate = INKEY_NORM;
225 #ifdef DEBUG
226 __CTRACE(__CTRACE_INPUT,
227 "inkey: WCASSEMBLING=>NORM, "
228 "start(%d), current(%d), end(%d)",
229 *start, *working, *end);
230 #endif /* DEBUG */
231 } else {
232 state = wstate = INKEY_BACKOUT;
233 #ifdef DEBUG
234 __CTRACE(__CTRACE_INPUT,
235 "inkey: WCASSEMBLING=>BACKOUT, "
236 "start(%d), current(%d), end(%d)",
237 *start, *working, *end);
238 #endif /* DEBUG */
239 }
240 return OK;
241 } else {
242 /* assembling wide characters */
243 inbuf[*end] = k;
244 *working = *end;
245 *end = (*end + 1) % MAX_CBUF_SIZE;
246 #ifdef DEBUG
247 __CTRACE(__CTRACE_INPUT,
248 "inkey: WCASSEMBLING[head(%d), "
249 "urrent(%d), tail(%d)]\n",
250 *start, *working, *end);
251 #endif /* DEBUG */
252 ret = (int)mbrtowc(wc, inbuf + (*working), 1,
253 &_cursesi_screen->sp);
254 #ifdef DEBUG
255 __CTRACE(__CTRACE_INPUT,
256 "inkey: mbrtowc returns %d, wc(%x)\n",
257 ret, *wc);
258 #endif /* DEBUG */
259 if (ret == -2) {
260 *working = (*working+1) % MAX_CBUF_SIZE;
261 continue;
262 }
263 if ( ret == 0 )
264 ret = 1;
265 if ( ret == -1 ) {
266 /* return the 1st character we know */
267 *wc = inbuf[*start];
268 *working = *start = (*start + 1) % MAX_CBUF_SIZE;
269 #ifdef DEBUG
270 __CTRACE(__CTRACE_INPUT,
271 "inkey: Invalid wide char(%x) "
272 "[head(%d), current(%d), "
273 "tail(%d)]\n",
274 *wc, *start, *working, *end);
275 #endif /* DEBUG */
276 } else { /* > 0 */
277 /* return the wide character */
278 *start = *working
279 = (*working + ret)%MAX_CBUF_SIZE;
280 #ifdef DEBUG
281 __CTRACE(__CTRACE_INPUT,
282 "inkey: Wide char found(%x) "
283 "[head(%d), current(%d), "
284 "tail(%d)]\n",
285 *wc, *start, *working, *end);
286 #endif /* DEBUG */
287 }
288
289 if (*start == *end) {
290 /* only one char processed */
291 state = wstate = INKEY_NORM;
292 #ifdef DEBUG
293 __CTRACE(__CTRACE_INPUT,
294 "inkey: WCASSEMBLING=>NORM, "
295 "start(%d), current(%d), end(%d)",
296 *start, *working, *end);
297 #endif /* DEBUG */
298 } else {
299 /* otherwise we must have more than
300 * 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 */
308 }
309 return OK;
310 }
311 } else {
312 fprintf(stderr, "Inkey wstate screwed - exiting!!!");
313 exit(2);
314 }
315
316 /*
317 * Check key has no special meaning and we have not
318 * timed out and the key has not been disabled
319 */
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 {
326 /* wide-character specific code */
327 #ifdef DEBUG
328 __CTRACE(__CTRACE_INPUT,
329 "inkey: Checking for wide char\n");
330 #endif /* DEBUG */
331 mbrtowc( NULL, NULL, 1, &_cursesi_screen->sp );
332 *working = *start;
333 mlen = *end > *working ?
334 *end - *working : MAX_CBUF_SIZE - *working;
335 if (!mlen)
336 return ERR;
337 #ifdef DEBUG
338 __CTRACE(__CTRACE_INPUT,
339 "inkey: Check wide char[head(%d), "
340 "current(%d), tail(%d), mlen(%ld)]\n",
341 *start, *working, *end, (long) mlen);
342 #endif /* DEBUG */
343 ret = (int)mbrtowc(wc, inbuf + (*working), mlen,
344 &_cursesi_screen->sp);
345 #ifdef DEBUG
346 __CTRACE(__CTRACE_INPUT,
347 "inkey: mbrtowc returns %d, wc(%x)\n", ret, *wc);
348 #endif /* DEBUG */
349 if (ret == -2 && *end < *working) {
350 /* second half of a wide character */
351 *working = 0;
352 mlen = *end;
353 if (mlen)
354 ret = (int)mbrtowc(wc, inbuf, mlen,
355 &_cursesi_screen->sp);
356 }
357 if (ret == -2 && wstate != INKEY_TIMEOUT) {
358 *working = (*working + (int) mlen)
359 % MAX_CBUF_SIZE;
360 wstate = INKEY_WCASSEMBLING;
361 continue;
362 }
363 if (ret == 0)
364 ret = 1;
365 if (ret == -1) {
366 /* return the first key we know about */
367 *wc = inbuf[*start];
368 *working = *start
369 = (*start + 1) % MAX_CBUF_SIZE;
370 #ifdef DEBUG
371 __CTRACE(__CTRACE_INPUT,
372 "inkey: Invalid wide char(%x)[head(%d), "
373 "current(%d), tail(%d)]\n",
374 *wc, *start, *working, *end);
375 #endif /* DEBUG */
376 } else { /* > 0 */
377 /* return the wide character */
378 *start = *working
379 = (*working + ret) % MAX_CBUF_SIZE;
380 #ifdef DEBUG
381 __CTRACE(__CTRACE_INPUT,
382 "inkey: Wide char found(%x)[head(%d), "
383 "current(%d), tail(%d)]\n",
384 *wc, *start, *working, *end);
385 #endif /* DEBUG */
386 }
387
388 if (*start == *end) { /* only one char processed */
389 state = wstate = INKEY_NORM;
390 #ifdef DEBUG
391 __CTRACE(__CTRACE_INPUT,
392 "inkey: Empty cbuf=>NORM, "
393 "start(%d), current(%d), end(%d)\n",
394 *start, *working, *end);
395 #endif /* DEBUG */
396 } else {
397 /* otherwise we must have more than one
398 * char to backout */
399 state = wstate = INKEY_BACKOUT;
400 #ifdef DEBUG
401 __CTRACE(__CTRACE_INPUT,
402 "inkey: Non-empty cbuf=>BACKOUT, "
403 "start(%d), current(%d), end(%d)\n",
404 *start, *working, *end);
405 #endif /* DEBUG */
406 }
407 return OK;
408 } else { /* must be part of a multikey sequence */
409 /* check for completed key sequence */
410 if (current->key[current->mapping[k]]->type
411 == KEYMAP_LEAF) {
412 /* eat the key sequence in cbuf */
413 *start = *working = ( *working + 1 )
414 % MAX_CBUF_SIZE;
415
416 /* check if inbuf empty now */
417 #ifdef DEBUG
418 __CTRACE(__CTRACE_INPUT,
419 "inkey: Key found(%s)\n",
420 key_name(current->key[mapping]->value.symbol));
421 #endif /* DEBUG */
422 if (*start == *end) {
423 /* if it is go back to normal */
424 state = wstate = INKEY_NORM;
425 #ifdef DEBUG
426 __CTRACE(__CTRACE_INPUT,
427 "[inkey]=>NORM, start(%d), "
428 "current(%d), end(%d)",
429 *start, *working, *end);
430 #endif /* DEBUG */
431 } else {
432 /* otherwise go to backout state */
433 state = wstate = INKEY_BACKOUT;
434 #ifdef DEBUG
435 __CTRACE(__CTRACE_INPUT,
436 "[inkey]=>BACKOUT, start(%d), "
437 "current(%d), end(%d)",
438 *start, *working, *end );
439 #endif /* DEBUG */
440 }
441
442 /* return the symbol */
443 *wc = current->key[mapping]->value.symbol;
444 return KEY_CODE_YES;
445 } else {
446 /* Step to next part of multi-key sequence */
447 current = current->key[current->mapping[k]]->value.next;
448 }
449 }
450 }
451 }
452 #endif /* HAVE_WCHAR */
453
454 /*
455 * get_wch --
456 * Read in a wide character from stdscr.
457 */
458 int
459 get_wch(wint_t *ch)
460 {
461 #ifndef HAVE_WCHAR
462 return ERR;
463 #else
464 return wget_wch(stdscr, ch);
465 #endif /* HAVE_WCHAR */
466 }
467
468 /*
469 * mvget_wch --
470 * Read in a character from stdscr at the given location.
471 */
472 int
473 mvget_wch(int y, int x, wint_t *ch)
474 {
475 #ifndef HAVE_WCHAR
476 return ERR;
477 #else
478 return mvwget_wch(stdscr, y, x, ch);
479 #endif /* HAVE_WCHAR */
480 }
481
482 /*
483 * mvwget_wch --
484 * Read in a character from stdscr at the given location in the
485 * given window.
486 */
487 int
488 mvwget_wch(WINDOW *win, int y, int x, wint_t *ch)
489 {
490 #ifndef HAVE_WCHAR
491 return ERR;
492 #else
493 if (wmove(win, y, x) == ERR)
494 return ERR;
495
496 return wget_wch(win, ch);
497 #endif /* HAVE_WCHAR */
498 }
499
500 /*
501 * wget_wch --
502 * Read in a wide character from the window.
503 */
504 int
505 wget_wch(WINDOW *win, wint_t *ch)
506 {
507 #ifndef HAVE_WCHAR
508 return ERR;
509 #else
510 int ret, weset;
511 int c;
512 FILE *infd = _cursesi_screen->infd;
513 cchar_t wc;
514 wchar_t inp, ws[2];
515
516 if (!(win->flags & __SCROLLOK)
517 && (win->flags & __FULLWIN)
518 && win->curx == win->maxx - 1
519 && win->cury == win->maxy - 1
520 && __echoit)
521 return ERR;
522
523 if (is_wintouched(win))
524 wrefresh(win);
525 #ifdef DEBUG
526 __CTRACE(__CTRACE_INPUT, "wget_wch: __echoit = %d, "
527 "__rawmode = %d, __nl = %d, flags = %#.4x\n",
528 __echoit, __rawmode, _cursesi_screen->nl, win->flags);
529 #endif
530 if (_cursesi_screen->resized) {
531 _cursesi_screen->resized = 0;
532 *ch = KEY_RESIZE;
533 return KEY_CODE_YES;
534 }
535 if (_cursesi_screen->unget_pos) {
536 #ifdef DEBUG
537 __CTRACE(__CTRACE_INPUT, "wget_wch returning char at %d\n",
538 _cursesi_screen->unget_pos);
539 #endif
540 _cursesi_screen->unget_pos--;
541 *ch = _cursesi_screen->unget_list[_cursesi_screen->unget_pos];
542 if (__echoit) {
543 ws[0] = *ch, ws[1] = L'\0';
544 setcchar(&wc, ws, win->wattr, 0, NULL);
545 wadd_wch(win, &wc);
546 }
547 return KEY_CODE_YES;
548 }
549 if (__echoit && !__rawmode) {
550 cbreak();
551 weset = 1;
552 } else
553 weset = 0;
554
555 __save_termios();
556
557 if (win->flags & __KEYPAD) {
558 switch (win->delay) {
559 case -1:
560 ret = inkey(&inp,
561 win->flags & __NOTIMEOUT ? 0 : 1, 0);
562 break;
563 case 0:
564 if (__nodelay() == ERR)
565 return ERR;
566 ret = inkey(&inp, 0, 0);
567 break;
568 default:
569 ret = inkey(&inp,
570 win->flags & __NOTIMEOUT ? 0 : 1,
571 win->delay);
572 break;
573 }
574 if ( ret == ERR )
575 return ERR;
576 } else {
577 switch (win->delay) {
578 case -1:
579 break;
580 case 0:
581 if (__nodelay() == ERR)
582 return ERR;
583 break;
584 default:
585 if (__timeout(win->delay) == ERR)
586 return ERR;
587 break;
588 }
589
590 c = getwchar();
591 if (feof(infd)) {
592 clearerr(infd);
593 __restore_termios();
594 return ERR; /* we have timed out */
595 }
596
597 if (ferror(infd)) {
598 clearerr(infd);
599 return ERR;
600 } else {
601 ret = c;
602 inp = c;
603 }
604 }
605 #ifdef DEBUG
606 if (inp > 255)
607 /* we have a key symbol - treat it differently */
608 /* XXXX perhaps __unctrl should be expanded to include
609 * XXXX the keysyms in the table....
610 */
611 __CTRACE(__CTRACE_INPUT, "wget_wch assembled keysym 0x%x\n",
612 inp);
613 else
614 __CTRACE(__CTRACE_INPUT, "wget_wch got '%s'\n", unctrl(inp));
615 #endif
616 if (win->delay > -1) {
617 if (__delay() == ERR)
618 return ERR;
619 }
620
621 __restore_termios();
622
623 if (__echoit) {
624 if ( ret == KEY_CODE_YES ) {
625 /* handle [DEL], [BS], and [LEFT] */
626 if ( win->curx &&
627 ( inp == KEY_DC ||
628 inp == KEY_BACKSPACE ||
629 inp == KEY_LEFT )) {
630 wmove( win, win->cury, win->curx - 1 );
631 wdelch( win );
632 }
633 } else {
634 ws[ 0 ] = inp, ws[ 1 ] = L'\0';
635 setcchar( &wc, ws, win->wattr, 0, NULL );
636 wadd_wch( win, &wc );
637 }
638 }
639
640 if (weset)
641 nocbreak();
642
643 if (_cursesi_screen->nl && inp == 13)
644 inp = 10;
645
646 *ch = inp;
647
648 if ( ret == KEY_CODE_YES )
649 return KEY_CODE_YES;
650 return inp < 0 ? ERR : OK;
651 #endif /* HAVE_WCHAR */
652 }
653
654 /*
655 * unget_wch --
656 * Put the wide character back into the input queue.
657 */
658 int
659 unget_wch(const wchar_t c)
660 {
661 return __unget((wint_t)c);
662 }
663