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