1 /* $NetBSD: get_wch.c,v 1.7 2009/11/01 22:11:27 dsl Exp $ */
4 * Copyright (c) 2005 The NetBSD Foundation Inc.
7 * This code is derived from code donated to the NetBSD Foundation
8 * by Ruibiao Qiu <ruibiao@arl.wustl.edu,ruibiao@gmail.com>.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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
37 #include <sys/cdefs.h>
39 __RCSID("$NetBSD: get_wch.c,v 1.7 2009/11/01 22:11:27 dsl Exp $");
47 #include "curses_private.h"
51 static short wstate
; /* state of the wcinkey function */
52 #endif /* HAVE_WCHAR */
53 extern short state
; /* storage declared in getch.c */
55 /* prototypes for private functions */
57 static int inkey(wchar_t *wc
, int to
, int delay
);
58 #endif /* HAVE_WCHAR */
62 * __init_get_wch - initialise all the pointers & structures needed to make
63 * get_wch work in keypad mode.
67 __init_get_wch(SCREEN
*screen
)
70 memset( &screen
->cbuf
, 0, MAX_CBUF_SIZE
* sizeof( int ));
71 screen
->cbuf_head
= screen
->cbuf_tail
= screen
->cbuf_cur
= 0;
73 #endif /* HAVE_WCHAR */
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.
83 inkey(wchar_t *wc
, int to
, int delay
)
86 int c
, mapping
, ret
= 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 ];
96 __CTRACE(__CTRACE_INPUT
, "inkey (%p, %d, %d)\n", wc
, to
, delay
);
98 for (;;) { /* loop until we get a complete key sequence */
99 if (wstate
== INKEY_NORM
) {
100 if (delay
&& __timeout(delay
) == ERR
)
108 if (delay
&& (__notimeout() == ERR
))
113 __CTRACE(__CTRACE_INPUT
,
114 "inkey (wstate normal) got '%s'\n", unctrl(k
));
118 *end
= ( *end
+ 1 ) % MAX_CBUF_SIZE
;
120 wstate
= INKEY_ASSEMBLING
; /* go to assembling state */
122 __CTRACE(__CTRACE_INPUT
,
123 "inkey: NORM=>ASSEMBLING: start(%d), "
124 "current(%d), end(%d)\n", *start
, *working
, *end
);
126 } else if (wstate
== INKEY_BACKOUT
) {
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
;
133 __CTRACE(__CTRACE_INPUT
,
134 "inkey: BACKOUT=>ASSEMBLING, start(%d), "
135 "current(%d), end(%d)\n",
136 *start
, *working
, *end
);
139 } else if (wstate
== INKEY_ASSEMBLING
) {
140 /* assembling a key sequence */
142 if (__timeout(to
? (ESCDELAY
/ 100) : delay
)
146 if (to
&& (__timeout(ESCDELAY
/ 100) == ERR
))
156 if ((to
|| delay
) && (__notimeout() == ERR
))
161 __CTRACE(__CTRACE_INPUT
,
162 "inkey (wstate assembling) got '%s'\n", unctrl(k
));
164 if (feof(infd
)) { /* inter-char T/O, start backout */
167 /* no chars in the buffer, restart */
171 wstate
= INKEY_TIMEOUT
;
173 __CTRACE(__CTRACE_INPUT
,
174 "inkey: ASSEMBLING=>TIMEOUT, start(%d), "
175 "current(%d), end(%d)\n",
176 *start
, *working
, *end
);
181 *end
= ( *end
+ 1 ) % MAX_CBUF_SIZE
;
183 __CTRACE(__CTRACE_INPUT
,
184 "inkey: ASSEMBLING: start(%d), "
185 "current(%d), end(%d)",
186 *start
, *working
, *end
);
189 } else if (wstate
== INKEY_WCASSEMBLING
) {
190 /* assembling a wide char sequence */
192 if (__timeout(to
? (ESCDELAY
/ 100) : delay
)
196 if (to
&& (__timeout(ESCDELAY
/ 100) == ERR
))
206 if ((to
|| delay
) && (__notimeout() == ERR
))
211 __CTRACE(__CTRACE_INPUT
,
212 "inkey (wstate wcassembling) got '%s'\n",
215 if (feof(infd
)) { /* inter-char T/O, start backout */
218 /* no chars in the buffer, restart */
223 = ( *start
+ 1 ) % MAX_CBUF_SIZE
;
224 if (*start
== *end
) {
225 state
= wstate
= INKEY_NORM
;
227 __CTRACE(__CTRACE_INPUT
,
228 "inkey: WCASSEMBLING=>NORM, "
229 "start(%d), current(%d), end(%d)",
230 *start
, *working
, *end
);
233 state
= wstate
= INKEY_BACKOUT
;
235 __CTRACE(__CTRACE_INPUT
,
236 "inkey: WCASSEMBLING=>BACKOUT, "
237 "start(%d), current(%d), end(%d)",
238 *start
, *working
, *end
);
243 /* assembling wide characters */
246 *end
= ( *end
+ 1 ) % MAX_CBUF_SIZE
;
248 __CTRACE(__CTRACE_INPUT
,
249 "inkey: WCASSEMBLING[head(%d), "
250 "urrent(%d), tail(%d)]\n",
251 *start
, *working
, *end
);
253 ret
= (int) mbrtowc( wc
, inbuf
+ (*working
), 1,
254 &_cursesi_screen
->sp
);
256 __CTRACE(__CTRACE_INPUT
,
257 "inkey: mbrtowc returns %d, wc(%x)\n",
261 *working
= (*working
+ 1)
268 /* return the 1st character we know */
269 *wc
= inbuf
[ *start
];
270 *working
= *start
= ( *start
+ 1 ) % MAX_CBUF_SIZE
;
272 __CTRACE(__CTRACE_INPUT
,
273 "inkey: Invalid wide char(%x) "
274 "[head(%d), current(%d), "
276 *wc
, *start
, *working
, *end
);
279 /* return the wide character */
281 = (*working
+ ret
)%MAX_CBUF_SIZE
;
283 __CTRACE(__CTRACE_INPUT
,
284 "inkey: Wide char found(%x) "
285 "[head(%d), current(%d), "
287 *wc
, *start
, *working
, *end
);
291 if (*start
== *end
) { /* only one char processed */
292 state
= wstate
= INKEY_NORM
;
294 __CTRACE(__CTRACE_INPUT
,
295 "inkey: WCASSEMBLING=>NORM, "
296 "start(%d), current(%d), end(%d)",
297 *start
, *working
, *end
);
300 /* otherwise we must have more than one char to backout */
301 state
= wstate
= INKEY_BACKOUT
;
303 __CTRACE(__CTRACE_INPUT
,
304 "inkey: WCASSEMBLING=>BACKOUT, "
305 "start(%d), current(%d), end(%d)",
306 *start
, *working
, *end
);
312 fprintf(stderr
, "Inkey wstate screwed - exiting!!!");
317 * Check key has no special meaning and we have not
318 * timed out and the key has not been disabled
320 mapping
= current
->mapping
[k
];
321 if (((wstate
== INKEY_TIMEOUT
) || (mapping
< 0))
322 || ((current
->key
[mapping
]->type
324 && (current
->key
[mapping
]->enable
== FALSE
))) {
325 /* wide character specific code */
327 __CTRACE(__CTRACE_INPUT
,
328 "inkey: Checking for wide char\n");
330 mbrtowc( NULL
, NULL
, 1, &_cursesi_screen
->sp
);
332 mlen
= *end
> *working
?
333 *end
- *working
: MAX_CBUF_SIZE
- *working
;
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
);
342 ret
= (int) mbrtowc( wc
, inbuf
+ (*working
), mlen
,
343 &_cursesi_screen
->sp
);
345 __CTRACE(__CTRACE_INPUT
,
346 "inkey: mbrtowc returns %d, wc(%x)\n", ret
, *wc
);
348 if ( ret
== -2 && *end
< *working
) {
349 /* second half of a wide character */
353 ret
= (int) mbrtowc( wc
, inbuf
, mlen
,
354 &_cursesi_screen
->sp
);
356 if ( ret
== -2 && wstate
!= INKEY_TIMEOUT
) {
357 *working
= (*working
+ (int) mlen
)
359 wstate
= INKEY_WCASSEMBLING
;
365 /* return the first key we know about */
366 *wc
= inbuf
[ *start
];
368 = ( *start
+ 1 ) % MAX_CBUF_SIZE
;
370 __CTRACE(__CTRACE_INPUT
,
371 "inkey: Invalid wide char(%x)[head(%d), "
372 "current(%d), tail(%d)]\n",
373 *wc
, *start
, *working
, *end
);
376 /* return the wide character */
378 = ( *working
+ ret
) % MAX_CBUF_SIZE
;
380 __CTRACE(__CTRACE_INPUT
,
381 "inkey: Wide char found(%x)[head(%d), "
382 "current(%d), tail(%d)]\n",
383 *wc
, *start
, *working
, *end
);
387 if (*start
== *end
) { /* only one char processed */
388 state
= wstate
= INKEY_NORM
;
390 __CTRACE(__CTRACE_INPUT
,
391 "inkey: Empty cbuf=>NORM, "
392 "start(%d), current(%d), end(%d)\n",
393 *start
, *working
, *end
);
396 /* otherwise we must have more than one char to backout */
397 state
= wstate
= INKEY_BACKOUT
;
399 __CTRACE(__CTRACE_INPUT
,
400 "inkey: Non-empty cbuf=>BACKOUT, "
401 "start(%d), current(%d), end(%d)\n",
402 *start
, *working
, *end
);
406 } else { /* must be part of a multikey sequence */
407 /* check for completed key sequence */
408 if (current
->key
[current
->mapping
[k
]]->type
410 /* eat the key sequence in cbuf */
411 *start
= *working
= ( *working
+ 1 ) % MAX_CBUF_SIZE
;
413 /* check if inbuf empty now */
415 __CTRACE(__CTRACE_INPUT
,
416 "inkey: Key found(%s)\n",
417 key_name(current
->key
[mapping
]->value
.symbol
));
419 if (*start
== *end
) {
420 /* if it is go back to normal */
421 state
= wstate
= INKEY_NORM
;
423 __CTRACE(__CTRACE_INPUT
,
424 "[inkey]=>NORM, start(%d), "
425 "current(%d), end(%d)",
426 *start
, *working
, *end
);
429 /* otherwise go to backout state */
430 state
= wstate
= INKEY_BACKOUT
;
432 __CTRACE(__CTRACE_INPUT
,
433 "[inkey]=>BACKOUT, start(%d), "
434 "current(%d), end(%d)",
435 *start
, *working
, *end
);
439 /* return the symbol */
440 *wc
= current
->key
[mapping
]->value
.symbol
;
443 /* Step to next part of multi-key sequence */
444 current
= current
->key
[current
->mapping
[k
]]->value
.next
;
449 #endif /* HAVE_WCHAR */
453 * Read in a wide character from stdscr.
461 return wget_wch(stdscr
, ch
);
462 #endif /* HAVE_WCHAR */
467 * Read in a character from stdscr at the given location.
470 mvget_wch(int y
, int x
, wint_t *ch
)
475 return mvwget_wch(stdscr
, y
, x
, ch
);
476 #endif /* HAVE_WCHAR */
481 * Read in a character from stdscr at the given location in the
485 mvwget_wch(WINDOW
*win
, int y
, int x
, wint_t *ch
)
490 if (wmove(win
, y
, x
) == ERR
)
493 return wget_wch(win
, ch
);
494 #endif /* HAVE_WCHAR */
499 * Read in a wide character from the window.
502 wget_wch(WINDOW
*win
, wint_t *ch
)
509 FILE *infd
= _cursesi_screen
->infd
;
511 wchar_t inp
, ws
[ 2 ];
513 if (!(win
->flags
& __SCROLLOK
) && (win
->flags
& __FULLWIN
)
514 && win
->curx
== win
->maxx
- 1
515 && win
->cury
== win
->maxy
- 1
519 if (is_wintouched(win
))
522 __CTRACE(__CTRACE_INPUT
, "wget_wch: __echoit = %d, "
523 "__rawmode = %d, __nl = %d, flags = %#.4x\n",
524 __echoit
, __rawmode
, _cursesi_screen
->nl
, win
->flags
);
526 if (_cursesi_screen
->resized
) {
527 _cursesi_screen
->resized
= 0;
531 if (_cursesi_screen
->unget_pos
) {
533 __CTRACE(__CTRACE_INPUT
, "wget_wch returning char at %d\n",
534 _cursesi_screen
->unget_pos
);
536 _cursesi_screen
->unget_pos
--;
537 *ch
= _cursesi_screen
->unget_list
[_cursesi_screen
->unget_pos
];
539 ws
[0] = *ch
, ws
[1] = L
'\0';
540 setcchar(&wc
, ws
, win
->wattr
, 0, NULL
);
545 if (__echoit
&& !__rawmode
) {
553 if (win
->flags
& __KEYPAD
) {
554 switch (win
->delay
) {
557 win
->flags
& __NOTIMEOUT
? 0 : 1, 0);
560 if (__nodelay() == ERR
)
562 ret
= inkey(&inp
, 0, 0);
566 win
->flags
& __NOTIMEOUT
? 0 : 1,
573 switch (win
->delay
) {
577 if (__nodelay() == ERR
)
581 if (__timeout(win
->delay
) == ERR
)
590 return ERR
; /* we have timed out */
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....
607 __CTRACE(__CTRACE_INPUT
, "wget_wch assembled keysym 0x%x\n",
610 __CTRACE(__CTRACE_INPUT
, "wget_wch got '%s'\n", unctrl(inp
));
612 if (win
->delay
> -1) {
613 if (__delay() == ERR
)
620 if ( ret
== KEY_CODE_YES
) {
621 /* handle [DEL], [BS], and [LEFT] */
624 inp
== KEY_BACKSPACE
||
626 wmove( win
, win
->cury
, win
->curx
- 1 );
630 ws
[ 0 ] = inp
, ws
[ 1 ] = L
'\0';
631 setcchar( &wc
, ws
, win
->wattr
, 0, NULL
);
632 wadd_wch( win
, &wc
);
639 if (_cursesi_screen
->nl
&& inp
== 13)
644 if ( ret
== KEY_CODE_YES
)
646 return ( inp
< 0 ? ERR
: OK
);
647 #endif /* HAVE_WCHAR */
652 * Put the wide character back into the input queue.
655 unget_wch(const wchar_t c
)
657 return __unget((wint_t) c
);