get_wch.c revision 1.10
1285101Semaste/* $NetBSD: get_wch.c,v 1.10 2012/06/29 10:40:29 blymn Exp $ */ 2285101Semaste 3285101Semaste/* 4285101Semaste * Copyright (c) 2005 The NetBSD Foundation Inc. 5285101Semaste * All rights reserved. 6285101Semaste * 7285101Semaste * This code is derived from code donated to the NetBSD Foundation 8285101Semaste * by Ruibiao Qiu <ruibiao@arl.wustl.edu,ruibiao@gmail.com>. 9285101Semaste * 10285101Semaste * 11285101Semaste * Redistribution and use in source and binary forms, with or without 12285101Semaste * modification, are permitted provided that the following conditions 13285101Semaste * are met: 14285101Semaste * 1. Redistributions of source code must retain the above copyright 15285101Semaste * notice, this list of conditions and the following disclaimer. 16285101Semaste * 2. Redistributions in binary form must reproduce the above copyright 17285101Semaste * notice, this list of conditions and the following disclaimer in the 18285101Semaste * documentation and/or other materials provided with the distribution. 19285101Semaste * 3. Neither the name of the NetBSD Foundation nor the names of its 20285101Semaste * contributors may be used to endorse or promote products derived 21285101Semaste * from this software without specific prior written permission. 22285101Semaste * 23285101Semaste * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND 24285101Semaste * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 25285101Semaste * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 26285101Semaste * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27285101Semaste * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28285101Semaste * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29285101Semaste * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30285101Semaste * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31285101Semaste * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32285101Semaste * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33285101Semaste * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34285101Semaste * SUCH DAMAGE. 35285101Semaste */ 36285101Semaste 37285101Semaste#include <sys/cdefs.h> 38285101Semaste#ifndef lint 39285101Semaste__RCSID("$NetBSD: get_wch.c,v 1.10 2012/06/29 10:40:29 blymn Exp $"); 40285101Semaste#endif /* not lint */ 41285101Semaste 42285101Semaste#include <string.h> 43285101Semaste#include <stdlib.h> 44285101Semaste#include <unistd.h> 45285101Semaste#include <stdio.h> 46285101Semaste#include "curses.h" 47285101Semaste#include "curses_private.h" 48285101Semaste#include "keymap.h" 49285101Semaste 50285101Semaste#ifdef HAVE_WCHAR 51285101Semastestatic short wstate; /* state of the wcinkey function */ 52285101Semaste#endif /* HAVE_WCHAR */ 53285101Semasteextern short state; /* storage declared in getch.c */ 54285101Semaste 55285101Semaste/* prototypes for private functions */ 56285101Semaste#ifdef HAVE_WCHAR 57285101Semastestatic int inkey(wchar_t *wc, int to, int delay); 58285101Semaste#endif /* HAVE_WCHAR */ 59285101Semaste 60285101Semaste#ifdef HAVE_WCHAR 61285101Semaste/* 62285101Semaste * __init_get_wch - initialise all the pointers & structures needed to make 63285101Semaste * get_wch work in keypad mode. 64285101Semaste * 65285101Semaste */ 66285101Semastevoid 67285101Semaste__init_get_wch(SCREEN *screen) 68285101Semaste{ 69285101Semaste wstate = INKEY_NORM; 70285101Semaste memset( &screen->cbuf, 0, sizeof(screen->cbuf)); 71285101Semaste screen->cbuf_head = screen->cbuf_tail = screen->cbuf_cur = 0; 72285101Semaste} 73285101Semaste#endif /* HAVE_WCHAR */ 74285101Semaste 75285101Semaste 76285101Semaste#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 = 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 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 return ERR; 562 ret = inkey(&inp, 0, 0); 563 break; 564 default: 565 ret = inkey(&inp, 566 win->flags & __NOTIMEOUT ? 0 : 1, 567 win->delay); 568 break; 569 } 570 if ( ret == ERR ) 571 return ERR; 572 } else { 573 switch (win->delay) { 574 case -1: 575 break; 576 case 0: 577 if (__nodelay() == ERR) 578 return ERR; 579 break; 580 default: 581 if (__timeout(win->delay) == ERR) 582 return ERR; 583 break; 584 } 585 586 c = getwchar(); 587 if (feof(infd)) { 588 clearerr(infd); 589 __restore_termios(); 590 return ERR; /* we have timed out */ 591 } 592 593 if (ferror(infd)) { 594 clearerr(infd); 595 return ERR; 596 } else { 597 ret = c; 598 inp = c; 599 } 600 } 601#ifdef DEBUG 602 if (inp > 255) 603 /* we have a key symbol - treat it differently */ 604 /* XXXX perhaps __unctrl should be expanded to include 605 * XXXX the keysyms in the table.... 606 */ 607 __CTRACE(__CTRACE_INPUT, "wget_wch assembled keysym 0x%x\n", 608 inp); 609 else 610 __CTRACE(__CTRACE_INPUT, "wget_wch got '%s'\n", unctrl(inp)); 611#endif 612 if (win->delay > -1) { 613 if (__delay() == ERR) 614 return ERR; 615 } 616 617 __restore_termios(); 618 619 if (__echoit) { 620 if ( ret == KEY_CODE_YES ) { 621 /* handle [DEL], [BS], and [LEFT] */ 622 if ( win->curx && 623 ( inp == KEY_DC || 624 inp == KEY_BACKSPACE || 625 inp == KEY_LEFT )) { 626 wmove( win, win->cury, win->curx - 1 ); 627 wdelch( win ); 628 } 629 } else { 630 ws[ 0 ] = inp, ws[ 1 ] = L'\0'; 631 setcchar( &wc, ws, win->wattr, 0, NULL ); 632 wadd_wch( win, &wc ); 633 } 634 } 635 636 if (weset) 637 nocbreak(); 638 639 if (_cursesi_screen->nl && inp == 13) 640 inp = 10; 641 642 *ch = inp; 643 644 if ( ret == KEY_CODE_YES ) 645 return KEY_CODE_YES; 646 return ( inp < 0 ? ERR : OK ); 647#endif /* HAVE_WCHAR */ 648} 649 650/* 651 * unget_wch -- 652 * Put the wide character back into the input queue. 653 */ 654int 655unget_wch(const wchar_t c) 656{ 657 return __unget((wint_t) c); 658} 659