get_wch.c revision 1.6
1/* $NetBSD: get_wch.c,v 1.6 2008/04/14 20:33:59 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.6 2008/04/14 20:33:59 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 50#ifdef HAVE_WCHAR 51static short wstate; /* state of the wcinkey function */ 52#endif /* HAVE_WCHAR */ 53extern short state; /* storage declared in getch.c */ 54 55/* prototypes for private functions */ 56#ifdef HAVE_WCHAR 57static 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 */ 66void 67__init_get_wch(SCREEN *screen) 68{ 69 wstate = INKEY_NORM; 70 memset( &screen->cbuf, 0, MAX_CBUF_SIZE * sizeof( int )); 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 */ 82static int 83inkey(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 = getchar(); 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 = getchar(); 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 = getchar(); 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 223 = ( *start + 1 ) % MAX_CBUF_SIZE; 224 if (*start == *end) { 225 state = wstate = INKEY_NORM; 226#ifdef DEBUG 227 __CTRACE(__CTRACE_INPUT, 228 "inkey: WCASSEMBLING=>NORM, " 229 "start(%d), current(%d), end(%d)", 230 *start, *working, *end); 231#endif /* DEBUG */ 232 } else { 233 state = wstate = INKEY_BACKOUT; 234#ifdef DEBUG 235 __CTRACE(__CTRACE_INPUT, 236 "inkey: WCASSEMBLING=>BACKOUT, " 237 "start(%d), current(%d), end(%d)", 238 *start, *working, *end); 239#endif /* DEBUG */ 240 } 241 return OK; 242 } else { 243 /* assembling wide characters */ 244 inbuf[ *end ] = k; 245 *working = *end; 246 *end = ( *end + 1 ) % MAX_CBUF_SIZE; 247#ifdef DEBUG 248 __CTRACE(__CTRACE_INPUT, 249 "inkey: WCASSEMBLING[head(%d), " 250 "urrent(%d), tail(%d)]\n", 251 *start, *working, *end); 252#endif /* DEBUG */ 253 ret = (int) mbrtowc( wc, inbuf + (*working), 1, 254 &_cursesi_screen->sp ); 255#ifdef DEBUG 256 __CTRACE(__CTRACE_INPUT, 257 "inkey: mbrtowc returns %d, wc(%x)\n", 258 ret, *wc ); 259#endif /* DEBUG */ 260 if ( ret == -2 ) { 261 *working = (*working + 1) 262 % 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) { /* only one char processed */ 292 state = wstate = INKEY_NORM; 293#ifdef DEBUG 294 __CTRACE(__CTRACE_INPUT, 295 "inkey: WCASSEMBLING=>NORM, " 296 "start(%d), current(%d), end(%d)", 297 *start, *working, *end); 298#endif /* DEBUG */ 299 } else { 300 /* otherwise we must have more than 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 /* wide character specific code */ 326#ifdef DEBUG 327 __CTRACE(__CTRACE_INPUT, 328 "inkey: Checking for wide char\n"); 329#endif /* DEBUG */ 330 mbrtowc( NULL, NULL, 1, &_cursesi_screen->sp ); 331 *working = *start; 332 mlen = *end > *working ? 333 *end - *working : MAX_CBUF_SIZE - *working; 334 if ( !mlen ) 335 return ERR; 336#ifdef DEBUG 337 __CTRACE(__CTRACE_INPUT, 338 "inkey: Check wide char[head(%d), " 339 "current(%d), tail(%d), mlen(%ld)]\n", 340 *start, *working, *end, (long) mlen); 341#endif /* DEBUG */ 342 ret = (int) mbrtowc( wc, inbuf + (*working), mlen, 343 &_cursesi_screen->sp ); 344#ifdef DEBUG 345 __CTRACE(__CTRACE_INPUT, 346 "inkey: mbrtowc returns %d, wc(%x)\n", ret, *wc); 347#endif /* DEBUG */ 348 if ( ret == -2 && *end < *working ) { 349 /* second half of a wide character */ 350 *working = 0; 351 mlen = *end; 352 if ( mlen ) 353 ret = (int) mbrtowc( wc, inbuf, mlen, 354 &_cursesi_screen->sp ); 355 } 356 if ( ret == -2 && wstate != INKEY_TIMEOUT ) { 357 *working = (*working + (int) mlen) 358 % 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(__CTRACE_INPUT, 371 "inkey: Invalid wide char(%x)[head(%d), " 372 "current(%d), tail(%d)]\n", 373 *wc, *start, *working, *end); 374#endif /* DEBUG */ 375 } else { /* > 0 */ 376 /* return the wide character */ 377 *start = *working 378 = ( *working + ret ) % MAX_CBUF_SIZE; 379#ifdef DEBUG 380 __CTRACE(__CTRACE_INPUT, 381 "inkey: Wide char found(%x)[head(%d), " 382 "current(%d), tail(%d)]\n", 383 *wc, *start, *working, *end); 384#endif /* DEBUG */ 385 } 386 387 if (*start == *end) { /* only one char processed */ 388 state = wstate = INKEY_NORM; 389#ifdef DEBUG 390 __CTRACE(__CTRACE_INPUT, 391 "inkey: Empty cbuf=>NORM, " 392 "start(%d), current(%d), end(%d)\n", 393 *start, *working, *end); 394#endif /* DEBUG */ 395 } else { 396 /* otherwise we must have more than one char to backout */ 397 state = wstate = INKEY_BACKOUT; 398#ifdef DEBUG 399 __CTRACE(__CTRACE_INPUT, 400 "inkey: Non-empty cbuf=>BACKOUT, " 401 "start(%d), current(%d), end(%d)\n", 402 *start, *working, *end); 403#endif /* DEBUG */ 404 } 405 return OK; 406 } else { /* must be part of a multikey sequence */ 407 /* check for completed key sequence */ 408 if (current->key[current->mapping[k]]->type 409 == KEYMAP_LEAF) { 410 /* eat the key sequence in cbuf */ 411 *start = *working = ( *working + 1 ) % MAX_CBUF_SIZE; 412 413 /* check if inbuf empty now */ 414#ifdef DEBUG 415 __CTRACE(__CTRACE_INPUT, 416 "inkey: Key found(%s)\n", 417 key_name(current->key[mapping]->value.symbol)); 418#endif /* DEBUG */ 419 if (*start == *end) { 420 /* if it is go back to normal */ 421 state = wstate = INKEY_NORM; 422#ifdef DEBUG 423 __CTRACE(__CTRACE_INPUT, 424 "[inkey]=>NORM, start(%d), " 425 "current(%d), end(%d)", 426 *start, *working, *end); 427#endif /* DEBUG */ 428 } else { 429 /* otherwise go to backout state */ 430 state = wstate = INKEY_BACKOUT; 431#ifdef DEBUG 432 __CTRACE(__CTRACE_INPUT, 433 "[inkey]=>BACKOUT, start(%d), " 434 "current(%d), end(%d)", 435 *start, *working, *end ); 436#endif /* DEBUG */ 437 } 438 439 /* return the symbol */ 440 *wc = current->key[mapping]->value.symbol; 441 return KEY_CODE_YES; 442 } else { 443 /* Step to next part of multi-key sequence */ 444 current = current->key[current->mapping[k]]->value.next; 445 } 446 } 447 } 448} 449#endif /* HAVE_WCHAR */ 450 451/* 452 * get_wch -- 453 * Read in a wide character from stdscr. 454 */ 455int 456get_wch(wint_t *ch) 457{ 458#ifndef HAVE_WCHAR 459 return ERR; 460#else 461 return wget_wch(stdscr, ch); 462#endif /* HAVE_WCHAR */ 463} 464 465/* 466 * mvget_wch -- 467 * Read in a character from stdscr at the given location. 468 */ 469int 470mvget_wch(int y, int x, wint_t *ch) 471{ 472#ifndef HAVE_WCHAR 473 return ERR; 474#else 475 return mvwget_wch(stdscr, y, x, ch); 476#endif /* HAVE_WCHAR */ 477} 478 479/* 480 * mvwget_wch -- 481 * Read in a character from stdscr at the given location in the 482 * given window. 483 */ 484int 485mvwget_wch(WINDOW *win, int y, int x, wint_t *ch) 486{ 487#ifndef HAVE_WCHAR 488 return ERR; 489#else 490 if (wmove(win, y, x) == ERR) 491 return ERR; 492 493 return wget_wch(win, ch); 494#endif /* HAVE_WCHAR */ 495} 496 497/* 498 * wget_wch -- 499 * Read in a wide character from the window. 500 */ 501int 502wget_wch(WINDOW *win, wint_t *ch) 503{ 504#ifndef HAVE_WCHAR 505 return ERR; 506#else 507 int ret, weset; 508 int c; 509 FILE *infd = _cursesi_screen->infd; 510 cchar_t wc; 511 wchar_t inp, ws[ 2 ]; 512 513 if (!(win->flags & __SCROLLOK) && (win->flags & __FULLWIN) 514 && win->curx == win->maxx - 1 515 && win->cury == win->maxy - 1 516 && __echoit) 517 return (ERR); 518 519 if (is_wintouched(win)) 520 wrefresh(win); 521#ifdef DEBUG 522 __CTRACE(__CTRACE_INPUT, "wget_wch: __echoit = %d, " 523 "__rawmode = %d, __nl = %d, flags = %#.4x\n", 524 __echoit, __rawmode, _cursesi_screen->nl, win->flags); 525#endif 526 if (_cursesi_screen->resized) { 527 _cursesi_screen->resized = 0; 528 *ch = KEY_RESIZE; 529 return KEY_CODE_YES; 530 } 531 if (_cursesi_screen->unget_pos) { 532#ifdef DEBUG 533 __CTRACE(__CTRACE_INPUT, "wget_wch returning char at %d\n", 534 _cursesi_screen->unget_pos); 535#endif 536 _cursesi_screen->unget_pos--; 537 *ch = _cursesi_screen->unget_list[_cursesi_screen->unget_pos]; 538 if (__echoit) { 539 ws[0] = *ch, ws[1] = L'\0'; 540 setcchar(&wc, ws, win->wattr, 0, NULL); 541 wadd_wch(win, &wc); 542 } 543 return KEY_CODE_YES; 544 } 545 if (__echoit && !__rawmode) { 546 cbreak(); 547 weset = 1; 548 } else 549 weset = 0; 550 551 __save_termios(); 552 553 if (win->flags & __KEYPAD) { 554 switch (win->delay) { 555 case -1: 556 ret = inkey(&inp, 557 win->flags & __NOTIMEOUT ? 0 : 1, 0); 558 break; 559 case 0: 560 if (__nodelay() == ERR) { 561 __restore_termios(); 562 return ERR; 563 } 564 ret = inkey(&inp, 0, 0); 565 break; 566 default: 567 ret = inkey(&inp, 568 win->flags & __NOTIMEOUT ? 0 : 1, 569 win->delay); 570 break; 571 } 572 if ( ret == ERR ) 573 return ERR; 574 } else { 575 switch (win->delay) { 576 case -1: 577 break; 578 case 0: 579 if (__nodelay() == ERR) { 580 __restore_termios(); 581 return ERR; 582 } 583 break; 584 default: 585 if (__timeout(win->delay) == ERR) { 586 __restore_termios(); 587 return ERR; 588 } 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 __restore_termios(); 621 return ERR; 622 } 623 } 624 625 __restore_termios(); 626 627 if (__echoit) { 628 if ( ret == KEY_CODE_YES ) { 629 /* handle [DEL], [BS], and [LEFT] */ 630 if ( win->curx && 631 ( inp == KEY_DC || 632 inp == KEY_BACKSPACE || 633 inp == KEY_LEFT )) { 634 wmove( win, win->cury, win->curx - 1 ); 635 wdelch( win ); 636 } 637 } else { 638 ws[ 0 ] = inp, ws[ 1 ] = L'\0'; 639 setcchar( &wc, ws, win->wattr, 0, NULL ); 640 wadd_wch( win, &wc ); 641 } 642 } 643 644 if (weset) 645 nocbreak(); 646 647 if (_cursesi_screen->nl && inp == 13) 648 inp = 10; 649 650 *ch = inp; 651 652 if ( ret == KEY_CODE_YES ) 653 return KEY_CODE_YES; 654 return ( inp < 0 ? ERR : OK ); 655#endif /* HAVE_WCHAR */ 656} 657 658/* 659 * unget_wch -- 660 * Put the wide character back into the input queue. 661 */ 662int 663unget_wch(const wchar_t c) 664{ 665 return __unget((wint_t) c); 666} 667