get_wch.c revision 1.5
1/* $NetBSD: get_wch.c,v 1.5 2007/12/08 18:38:11 jdc 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@arl.wustl.edu,ruibiao@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.5 2007/12/08 18:38:11 jdc 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 50static short wstate; /* state of the wcinkey function */ 51extern short state; /* storage declared in getch.c */ 52 53/* prototypes for private functions */ 54static int inkey(wchar_t *wc, int to, int delay); 55 56/* 57 * __init_get_wch - initialise all the pointers & structures needed to make 58 * get_wch work in keypad mode. 59 * 60 */ 61void 62__init_get_wch(SCREEN *screen) 63{ 64#ifndef HAVE_WCHAR 65 return; 66#else 67 wstate = INKEY_NORM; 68 memset( &screen->cbuf, 0, MAX_CBUF_SIZE * sizeof( int )); 69 screen->cbuf_head = screen->cbuf_tail = screen->cbuf_cur = 0; 70#endif /* HAVE_WCHAR */ 71} 72 73 74/* 75 * inkey - do the work to process keyboard input, check for multi-key 76 * sequences and return the appropriate symbol if we get a match. 77 * 78 */ 79 80int 81inkey(wchar_t *wc, int to, int delay) 82{ 83#ifndef HAVE_WCHAR 84 return ERR; 85#else 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 = getchar(); 104 if (c == WEOF) { 105 clearerr(infd); 106 return ERR; 107 } 108 109 if (delay && (__notimeout() == ERR)) 110 return ERR; 111 112 k = (wchar_t) c; 113#ifdef DEBUG 114 __CTRACE(__CTRACE_INPUT, 115 "inkey (wstate normal) got '%s'\n", unctrl(k)); 116#endif 117 118 inbuf[ *end ] = k; 119 *end = ( *end + 1 ) % MAX_CBUF_SIZE; 120 *working = *start; 121 wstate = INKEY_ASSEMBLING; /* go to assembling state */ 122#ifdef DEBUG 123 __CTRACE(__CTRACE_INPUT, 124 "inkey: NORM=>ASSEMBLING: start(%d), " 125 "current(%d), end(%d)\n", *start, *working, *end); 126#endif /* DEBUG */ 127 } else if (wstate == INKEY_BACKOUT) { 128 k = inbuf[*working]; 129 *working = ( *working + 1 ) % MAX_CBUF_SIZE; 130 if (*working == *end) { /* see if run out of keys */ 131 /* if so, switch to assembling */ 132 wstate = INKEY_ASSEMBLING; 133#ifdef DEBUG 134 __CTRACE(__CTRACE_INPUT, 135 "inkey: BACKOUT=>ASSEMBLING, start(%d), " 136 "current(%d), end(%d)\n", 137 *start, *working, *end); 138#endif /* DEBUG */ 139 } 140 } else if (wstate == INKEY_ASSEMBLING) { 141 /* assembling a key sequence */ 142 if (delay) { 143 if (__timeout(to ? (ESCDELAY / 100) : delay) 144 == ERR) 145 return ERR; 146 } else { 147 if (to && (__timeout(ESCDELAY / 100) == ERR)) 148 return ERR; 149 } 150 151 c = getchar(); 152 if (ferror(infd)) { 153 clearerr(infd); 154 return ERR; 155 } 156 157 if ((to || delay) && (__notimeout() == ERR)) 158 return ERR; 159 160 k = (wchar_t) c; 161#ifdef DEBUG 162 __CTRACE(__CTRACE_INPUT, 163 "inkey (wstate assembling) got '%s'\n", unctrl(k)); 164#endif /* DEBUG */ 165 if (feof(infd)) { /* inter-char T/O, start backout */ 166 clearerr(infd); 167 if (*start == *end) 168 /* no chars in the buffer, restart */ 169 continue; 170 171 k = inbuf[*start]; 172 wstate = INKEY_TIMEOUT; 173#ifdef DEBUG 174 __CTRACE(__CTRACE_INPUT, 175 "inkey: ASSEMBLING=>TIMEOUT, start(%d), " 176 "current(%d), end(%d)\n", 177 *start, *working, *end); 178#endif /* DEBUG */ 179 } else { 180 inbuf[ *end ] = k; 181 *working = *end; 182 *end = ( *end + 1 ) % MAX_CBUF_SIZE; 183#ifdef DEBUG 184 __CTRACE(__CTRACE_INPUT, 185 "inkey: ASSEMBLING: start(%d), " 186 "current(%d), end(%d)", 187 *start, *working, *end); 188#endif /* DEBUG */ 189 } 190 } else if (wstate == INKEY_WCASSEMBLING) { 191 /* assembling a wide char sequence */ 192 if (delay) { 193 if (__timeout(to ? (ESCDELAY / 100) : delay) 194 == ERR) 195 return ERR; 196 } else { 197 if (to && (__timeout(ESCDELAY / 100) == ERR)) 198 return ERR; 199 } 200 201 c = getchar(); 202 if (ferror(infd)) { 203 clearerr(infd); 204 return ERR; 205 } 206 207 if ((to || delay) && (__notimeout() == ERR)) 208 return ERR; 209 210 k = (wchar_t) c; 211#ifdef DEBUG 212 __CTRACE(__CTRACE_INPUT, 213 "inkey (wstate wcassembling) got '%s'\n", 214 unctrl(k)); 215#endif 216 if (feof(infd)) { /* inter-char T/O, start backout */ 217 clearerr(infd); 218 if (*start == *end) 219 /* no chars in the buffer, restart */ 220 continue; 221 222 *wc = inbuf[*start]; 223 *working = *start 224 = ( *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) 263 % MAX_CBUF_SIZE; 264 continue; 265 } 266 if ( ret == 0 ) 267 ret = 1; 268 if ( ret == -1 ) { 269 /* return the 1st character we know */ 270 *wc = inbuf[ *start ]; 271 *working = *start = ( *start + 1 ) % MAX_CBUF_SIZE; 272#ifdef DEBUG 273 __CTRACE(__CTRACE_INPUT, 274 "inkey: Invalid wide char(%x) " 275 "[head(%d), current(%d), " 276 "tail(%d)]\n", 277 *wc, *start, *working, *end); 278#endif /* DEBUG */ 279 } else { /* > 0 */ 280 /* return the wide character */ 281 *start = *working 282 = (*working + ret)%MAX_CBUF_SIZE; 283#ifdef DEBUG 284 __CTRACE(__CTRACE_INPUT, 285 "inkey: Wide char found(%x) " 286 "[head(%d), current(%d), " 287 "tail(%d)]\n", 288 *wc, *start, *working, *end); 289#endif /* DEBUG */ 290 } 291 292 if (*start == *end) { /* 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 one char to backout */ 302 state = wstate = INKEY_BACKOUT; 303#ifdef DEBUG 304 __CTRACE(__CTRACE_INPUT, 305 "inkey: WCASSEMBLING=>BACKOUT, " 306 "start(%d), current(%d), end(%d)", 307 *start, *working, *end); 308#endif /* DEBUG */ 309 } 310 return OK; 311 } 312 } else { 313 fprintf(stderr, "Inkey wstate screwed - exiting!!!"); 314 exit(2); 315 } 316 317 /* 318 * Check key has no special meaning and we have not 319 * timed out and the key has not been disabled 320 */ 321 mapping = current->mapping[k]; 322 if (((wstate == INKEY_TIMEOUT) || (mapping < 0)) 323 || ((current->key[mapping]->type 324 == KEYMAP_LEAF) 325 && (current->key[mapping]->enable == FALSE))) { 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 char to backout */ 398 state = wstate = INKEY_BACKOUT; 399#ifdef DEBUG 400 __CTRACE(__CTRACE_INPUT, 401 "inkey: Non-empty cbuf=>BACKOUT, " 402 "start(%d), current(%d), end(%d)\n", 403 *start, *working, *end); 404#endif /* DEBUG */ 405 } 406 return OK; 407 } else { /* must be part of a multikey sequence */ 408 /* check for completed key sequence */ 409 if (current->key[current->mapping[k]]->type 410 == KEYMAP_LEAF) { 411 /* eat the key sequence in cbuf */ 412 *start = *working = ( *working + 1 ) % MAX_CBUF_SIZE; 413 414 /* check if inbuf empty now */ 415#ifdef DEBUG 416 __CTRACE(__CTRACE_INPUT, 417 "inkey: Key found(%s)\n", 418 key_name(current->key[mapping]->value.symbol)); 419#endif /* DEBUG */ 420 if (*start == *end) { 421 /* if it is go back to normal */ 422 state = wstate = INKEY_NORM; 423#ifdef DEBUG 424 __CTRACE(__CTRACE_INPUT, 425 "[inkey]=>NORM, start(%d), " 426 "current(%d), end(%d)", 427 *start, *working, *end); 428#endif /* DEBUG */ 429 } else { 430 /* otherwise go to backout state */ 431 state = wstate = INKEY_BACKOUT; 432#ifdef DEBUG 433 __CTRACE(__CTRACE_INPUT, 434 "[inkey]=>BACKOUT, start(%d), " 435 "current(%d), end(%d)", 436 *start, *working, *end ); 437#endif /* DEBUG */ 438 } 439 440 /* return the symbol */ 441 *wc = current->key[mapping]->value.symbol; 442 return KEY_CODE_YES; 443 } else { 444 /* Step to next part of multi-key sequence */ 445 current = current->key[current->mapping[k]]->value.next; 446 } 447 } 448 } 449#endif /* HAVE_WCHAR */ 450} 451 452/* 453 * get_wch -- 454 * Read in a wide character from stdscr. 455 */ 456int 457get_wch(wint_t *ch) 458{ 459#ifndef HAVE_WCHAR 460 return ERR; 461#else 462 return wget_wch(stdscr, ch); 463#endif /* HAVE_WCHAR */ 464} 465 466/* 467 * mvget_wch -- 468 * Read in a character from stdscr at the given location. 469 */ 470int 471mvget_wch(int y, int x, wint_t *ch) 472{ 473#ifndef HAVE_WCHAR 474 return ERR; 475#else 476 return mvwget_wch(stdscr, y, x, ch); 477#endif /* HAVE_WCHAR */ 478} 479 480/* 481 * mvwget_wch -- 482 * Read in a character from stdscr at the given location in the 483 * given window. 484 */ 485int 486mvwget_wch(WINDOW *win, int y, int x, wint_t *ch) 487{ 488#ifndef HAVE_WCHAR 489 return ERR; 490#else 491 if (wmove(win, y, x) == ERR) 492 return ERR; 493 494 return wget_wch(win, ch); 495#endif /* HAVE_WCHAR */ 496} 497 498/* 499 * wget_wch -- 500 * Read in a wide character from the window. 501 */ 502int 503wget_wch(WINDOW *win, wint_t *ch) 504{ 505#ifndef HAVE_WCHAR 506 return ERR; 507#else 508 int ret, weset; 509 int c; 510 FILE *infd = _cursesi_screen->infd; 511 cchar_t wc; 512 wchar_t inp, ws[ 2 ]; 513 514 if (!(win->flags & __SCROLLOK) && (win->flags & __FULLWIN) 515 && win->curx == win->maxx - 1 516 && win->cury == win->maxy - 1 517 && __echoit) 518 return (ERR); 519 520 if (is_wintouched(win)) 521 wrefresh(win); 522#ifdef DEBUG 523 __CTRACE(__CTRACE_INPUT, "wget_wch: __echoit = %d, " 524 "__rawmode = %d, __nl = %d, flags = %#.4x\n", 525 __echoit, __rawmode, _cursesi_screen->nl, win->flags); 526#endif 527 if (_cursesi_screen->resized) { 528 _cursesi_screen->resized = 0; 529 *ch = KEY_RESIZE; 530 return KEY_CODE_YES; 531 } 532 if (_cursesi_screen->unget_pos) { 533#ifdef DEBUG 534 __CTRACE(__CTRACE_INPUT, "wget_wch returning char at %d\n", 535 _cursesi_screen->unget_pos); 536#endif 537 _cursesi_screen->unget_pos--; 538 *ch = _cursesi_screen->unget_list[_cursesi_screen->unget_pos]; 539 if (__echoit) { 540 ws[0] = *ch, ws[1] = L'\0'; 541 setcchar(&wc, ws, win->wattr, 0, NULL); 542 wadd_wch(win, &wc); 543 } 544 return KEY_CODE_YES; 545 } 546 if (__echoit && !__rawmode) { 547 cbreak(); 548 weset = 1; 549 } else 550 weset = 0; 551 552 __save_termios(); 553 554 if (win->flags & __KEYPAD) { 555 switch (win->delay) { 556 case -1: 557 ret = inkey(&inp, 558 win->flags & __NOTIMEOUT ? 0 : 1, 0); 559 break; 560 case 0: 561 if (__nodelay() == ERR) { 562 __restore_termios(); 563 return ERR; 564 } 565 ret = inkey(&inp, 0, 0); 566 break; 567 default: 568 ret = inkey(&inp, 569 win->flags & __NOTIMEOUT ? 0 : 1, 570 win->delay); 571 break; 572 } 573 if ( ret == ERR ) 574 return ERR; 575 } else { 576 switch (win->delay) { 577 case -1: 578 break; 579 case 0: 580 if (__nodelay() == ERR) { 581 __restore_termios(); 582 return ERR; 583 } 584 break; 585 default: 586 if (__timeout(win->delay) == ERR) { 587 __restore_termios(); 588 return ERR; 589 } 590 break; 591 } 592 593 c = getwchar(); 594 if (feof(infd)) { 595 clearerr(infd); 596 __restore_termios(); 597 return ERR; /* we have timed out */ 598 } 599 600 if (ferror(infd)) { 601 clearerr(infd); 602 return ERR; 603 } else { 604 ret = c; 605 inp = c; 606 } 607 } 608#ifdef DEBUG 609 if (inp > 255) 610 /* we have a key symbol - treat it differently */ 611 /* XXXX perhaps __unctrl should be expanded to include 612 * XXXX the keysyms in the table.... 613 */ 614 __CTRACE(__CTRACE_INPUT, "wget_wch assembled keysym 0x%x\n", 615 inp); 616 else 617 __CTRACE(__CTRACE_INPUT, "wget_wch got '%s'\n", unctrl(inp)); 618#endif 619 if (win->delay > -1) { 620 if (__delay() == ERR) { 621 __restore_termios(); 622 return ERR; 623 } 624 } 625 626 __restore_termios(); 627 628 if (__echoit) { 629 if ( ret == KEY_CODE_YES ) { 630 /* handle [DEL], [BS], and [LEFT] */ 631 if ( win->curx && 632 ( inp == KEY_DC || 633 inp == KEY_BACKSPACE || 634 inp == KEY_LEFT )) { 635 wmove( win, win->cury, win->curx - 1 ); 636 wdelch( win ); 637 } 638 } else { 639 ws[ 0 ] = inp, ws[ 1 ] = L'\0'; 640 setcchar( &wc, ws, win->wattr, 0, NULL ); 641 wadd_wch( win, &wc ); 642 } 643 } 644 645 if (weset) 646 nocbreak(); 647 648 if (_cursesi_screen->nl && inp == 13) 649 inp = 10; 650 651 *ch = inp; 652 653 if ( ret == KEY_CODE_YES ) 654 return KEY_CODE_YES; 655 return ( inp < 0 ? ERR : OK ); 656#endif /* HAVE_WCHAR */ 657} 658 659/* 660 * unget_wch -- 661 * Put the wide character back into the input queue. 662 */ 663int 664unget_wch(const wchar_t c) 665{ 666 return __unget((wint_t) c); 667} 668