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