GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libcurses/base/lib_getch.c Lines: 0 144 0.0 %
Date: 2017-11-13 Branches: 0 144 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: lib_getch.c,v 1.11 2010/01/12 23:22:05 nicm Exp $ */
2
3
/****************************************************************************
4
 * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc.              *
5
 *                                                                          *
6
 * Permission is hereby granted, free of charge, to any person obtaining a  *
7
 * copy of this software and associated documentation files (the            *
8
 * "Software"), to deal in the Software without restriction, including      *
9
 * without limitation the rights to use, copy, modify, merge, publish,      *
10
 * distribute, distribute with modifications, sublicense, and/or sell       *
11
 * copies of the Software, and to permit persons to whom the Software is    *
12
 * furnished to do so, subject to the following conditions:                 *
13
 *                                                                          *
14
 * The above copyright notice and this permission notice shall be included  *
15
 * in all copies or substantial portions of the Software.                   *
16
 *                                                                          *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
18
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
19
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
20
 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
21
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
22
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
23
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
24
 *                                                                          *
25
 * Except as contained in this notice, the name(s) of the above copyright   *
26
 * holders shall not be used in advertising or otherwise to promote the     *
27
 * sale, use or other dealings in this Software without prior written       *
28
 * authorization.                                                           *
29
 ****************************************************************************/
30
31
/****************************************************************************
32
 *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
33
 *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
34
 *     and: Thomas E. Dickey                        1996-on                 *
35
 ****************************************************************************/
36
37
/*
38
**	lib_getch.c
39
**
40
**	The routine getch().
41
**
42
*/
43
44
#include <curses.priv.h>
45
46
MODULE_ID("$Id: lib_getch.c,v 1.11 2010/01/12 23:22:05 nicm Exp $")
47
48
#include <fifo_defs.h>
49
50
#if USE_REENTRANT
51
#define GetEscdelay(sp) (sp)->_ESCDELAY
52
NCURSES_EXPORT(int)
53
NCURSES_PUBLIC_VAR(ESCDELAY) (void)
54
{
55
    return SP ? GetEscdelay(SP) : 1000;
56
}
57
#else
58
#define GetEscdelay(sp) ESCDELAY
59
NCURSES_EXPORT_VAR(int)
60
ESCDELAY = 1000;		/* max interval betw. chars in funkeys, in millisecs */
61
#endif
62
63
#if NCURSES_EXT_FUNCS
64
NCURSES_EXPORT(int)
65
set_escdelay(int value)
66
{
67
    int code = OK;
68
#if USE_REENTRANT
69
    if (SP) {
70
	SP->_ESCDELAY = value;
71
    } else {
72
	code = ERR;
73
    }
74
#else
75
    ESCDELAY = value;
76
#endif
77
    return code;
78
}
79
#endif
80
81
static int
82
_nc_use_meta(WINDOW *win)
83
{
84
    SCREEN *sp = _nc_screen_of(win);
85
    return (sp ? sp->_use_meta : 0);
86
}
87
88
#ifdef NCURSES_WGETCH_EVENTS
89
#define TWAIT_MASK 7
90
#else
91
#define TWAIT_MASK 3
92
#endif
93
94
/*
95
 * Check for mouse activity, returning nonzero if we find any.
96
 */
97
static int
98
check_mouse_activity(SCREEN *sp, int delay EVENTLIST_2nd(_nc_eventlist * evl))
99
{
100
    int rc;
101
102
#if USE_SYSMOUSE
103
    if ((sp->_mouse_type == M_SYSMOUSE)
104
	&& (sp->_sysmouse_head < sp->_sysmouse_tail)) {
105
	return 2;
106
    }
107
#endif
108
    rc = _nc_timed_wait(sp, TWAIT_MASK, delay, (int *) 0 EVENTLIST_2nd(evl));
109
#if USE_SYSMOUSE
110
    if ((sp->_mouse_type == M_SYSMOUSE)
111
	&& (sp->_sysmouse_head < sp->_sysmouse_tail)
112
	&& (rc == 0)
113
	&& (errno == EINTR)) {
114
	rc |= 2;
115
    }
116
#endif
117
    return rc;
118
}
119
120
static NCURSES_INLINE int
121
fifo_peek(SCREEN *sp)
122
{
123
    int ch = sp->_fifo[peek];
124
    TR(TRACE_IEVENT, ("peeking at %d", peek));
125
126
    p_inc();
127
    return ch;
128
}
129
130
static NCURSES_INLINE int
131
fifo_pull(SCREEN *sp)
132
{
133
    int ch;
134
    ch = sp->_fifo[head];
135
    TR(TRACE_IEVENT, ("pulling %s from %d", _nc_tracechar(sp, ch), head));
136
137
    if (peek == head) {
138
	h_inc();
139
	peek = head;
140
    } else
141
	h_inc();
142
143
#ifdef TRACE
144
    if (USE_TRACEF(TRACE_IEVENT)) {
145
	_nc_fifo_dump(sp);
146
	_nc_unlock_global(tracef);
147
    }
148
#endif
149
    return ch;
150
}
151
152
static NCURSES_INLINE int
153
fifo_push(SCREEN *sp EVENTLIST_2nd(_nc_eventlist * evl))
154
{
155
    int n;
156
    int ch = 0;
157
    int mask = 0;
158
159
    (void) mask;
160
    if (tail == -1)
161
	return ERR;
162
163
#ifdef HIDE_EINTR
164
  again:
165
    errno = 0;
166
#endif
167
168
#ifdef NCURSES_WGETCH_EVENTS
169
    if (evl
170
#if USE_GPM_SUPPORT || USE_EMX_MOUSE || USE_SYSMOUSE
171
	|| (sp->_mouse_fd >= 0)
172
#endif
173
	) {
174
	mask = check_mouse_activity(sp, -1 EVENTLIST_2nd(evl));
175
    } else
176
	mask = 0;
177
178
    if (mask & 4) {
179
	T(("fifo_push: ungetch KEY_EVENT"));
180
	_nc_ungetch(sp, KEY_EVENT);
181
	return KEY_EVENT;
182
    }
183
#elif USE_GPM_SUPPORT || USE_EMX_MOUSE || USE_SYSMOUSE
184
    if (sp->_mouse_fd >= 0) {
185
	mask = check_mouse_activity(sp, -1 EVENTLIST_2nd(evl));
186
    }
187
#endif
188
189
#if USE_GPM_SUPPORT || USE_EMX_MOUSE
190
    if ((sp->_mouse_fd >= 0) && (mask & 2)) {
191
	sp->_mouse_event(sp);
192
	ch = KEY_MOUSE;
193
	n = 1;
194
    } else
195
#endif
196
#if USE_SYSMOUSE
197
	if ((sp->_mouse_type == M_SYSMOUSE)
198
	    && (sp->_sysmouse_head < sp->_sysmouse_tail)) {
199
	sp->_mouse_event(sp);
200
	ch = KEY_MOUSE;
201
	n = 1;
202
    } else if ((sp->_mouse_type == M_SYSMOUSE)
203
	       && (mask <= 0) && errno == EINTR) {
204
	sp->_mouse_event(sp);
205
	ch = KEY_MOUSE;
206
	n = 1;
207
    } else
208
#endif
209
    {				/* Can block... */
210
	unsigned char c2 = 0;
211
	n = read(sp->_ifd, &c2, 1);
212
	ch = c2;
213
    }
214
215
#ifdef HIDE_EINTR
216
    /*
217
     * Under System V curses with non-restarting signals, getch() returns
218
     * with value ERR when a handled signal keeps it from completing.
219
     * If signals restart system calls, OTOH, the signal is invisible
220
     * except to its handler.
221
     *
222
     * We don't want this difference to show.  This piece of code
223
     * tries to make it look like we always have restarting signals.
224
     */
225
    if (n <= 0 && errno == EINTR)
226
	goto again;
227
#endif
228
229
    if ((n == -1) || (n == 0)) {
230
	TR(TRACE_IEVENT, ("read(%d,&ch,1)=%d, errno=%d", sp->_ifd, n, errno));
231
	ch = ERR;
232
    }
233
    TR(TRACE_IEVENT, ("read %d characters", n));
234
235
    sp->_fifo[tail] = ch;
236
    sp->_fifohold = 0;
237
    if (head == -1)
238
	head = peek = tail;
239
    t_inc();
240
    TR(TRACE_IEVENT, ("pushed %s at %d", _nc_tracechar(sp, ch), tail));
241
#ifdef TRACE
242
    if (USE_TRACEF(TRACE_IEVENT)) {
243
	_nc_fifo_dump(sp);
244
	_nc_unlock_global(tracef);
245
    }
246
#endif
247
    return ch;
248
}
249
250
static NCURSES_INLINE void
251
fifo_clear(SCREEN *sp)
252
{
253
    memset(sp->_fifo, 0, sizeof(sp->_fifo));
254
    head = -1;
255
    tail = peek = 0;
256
}
257
258
static int kgetch(SCREEN *EVENTLIST_2nd(_nc_eventlist * evl));
259
260
static void
261
recur_wrefresh(WINDOW *win)
262
{
263
#ifdef USE_PTHREADS
264
    SCREEN *sp = _nc_screen_of(win);
265
    if (_nc_use_pthreads && sp != SP) {
266
	SCREEN *save_SP;
267
268
	/* temporarily switch to the window's screen to check/refresh */
269
	_nc_lock_global(curses);
270
	save_SP = SP;
271
	_nc_set_screen(sp);
272
	recur_wrefresh(win);
273
	_nc_set_screen(save_SP);
274
	_nc_unlock_global(curses);
275
    } else
276
#endif
277
	if ((is_wintouched(win) || (win->_flags & _HASMOVED))
278
	    && !(win->_flags & _ISPAD)) {
279
	wrefresh(win);
280
    }
281
}
282
283
static int
284
recur_wgetnstr(WINDOW *win, char *buf)
285
{
286
    SCREEN *sp = _nc_screen_of(win);
287
    int rc;
288
289
    if (sp != 0) {
290
#ifdef USE_PTHREADS
291
	if (_nc_use_pthreads && sp != SP) {
292
	    SCREEN *save_SP;
293
294
	    /* temporarily switch to the window's screen to get cooked input */
295
	    _nc_lock_global(curses);
296
	    save_SP = SP;
297
	    _nc_set_screen(sp);
298
	    rc = recur_wgetnstr(win, buf);
299
	    _nc_set_screen(save_SP);
300
	    _nc_unlock_global(curses);
301
	} else
302
#endif
303
	{
304
	    sp->_called_wgetch = TRUE;
305
	    rc = wgetnstr(win, buf, MAXCOLUMNS);
306
	    sp->_called_wgetch = FALSE;
307
	}
308
    } else {
309
	rc = ERR;
310
    }
311
    return rc;
312
}
313
314
NCURSES_EXPORT(int)
315
_nc_wgetch(WINDOW *win,
316
	   unsigned long *result,
317
	   int use_meta
318
	   EVENTLIST_2nd(_nc_eventlist * evl))
319
{
320
    SCREEN *sp;
321
    int ch;
322
#ifdef NCURSES_WGETCH_EVENTS
323
    long event_delay = -1;
324
#endif
325
326
    T((T_CALLED("_nc_wgetch(%p)"), win));
327
328
    *result = 0;
329
330
    sp = _nc_screen_of(win);
331
    if (win == 0 || sp == 0) {
332
	returnCode(ERR);
333
    }
334
335
    if (cooked_key_in_fifo()) {
336
	recur_wrefresh(win);
337
	*result = fifo_pull(sp);
338
	returnCode(*result >= KEY_MIN ? KEY_CODE_YES : OK);
339
    }
340
#ifdef NCURSES_WGETCH_EVENTS
341
    if (evl && (evl->count == 0))
342
	evl = NULL;
343
    event_delay = _nc_eventlist_timeout(evl);
344
#endif
345
346
    /*
347
     * Handle cooked mode.  Grab a string from the screen,
348
     * stuff its contents in the FIFO queue, and pop off
349
     * the first character to return it.
350
     */
351
    if (head == -1 &&
352
	!sp->_notty &&
353
	!sp->_raw &&
354
	!sp->_cbreak &&
355
	!sp->_called_wgetch) {
356
	char buf[MAXCOLUMNS], *bufp;
357
	int rc;
358
359
	TR(TRACE_IEVENT, ("filling queue in cooked mode"));
360
361
	rc = recur_wgetnstr(win, buf);
362
363
	/* ungetch in reverse order */
364
#ifdef NCURSES_WGETCH_EVENTS
365
	if (rc != KEY_EVENT)
366
#endif
367
	    _nc_ungetch(sp, '\n');
368
	for (bufp = buf + strlen(buf); bufp > buf; bufp--)
369
	    _nc_ungetch(sp, bufp[-1]);
370
371
#ifdef NCURSES_WGETCH_EVENTS
372
	/* Return it first */
373
	if (rc == KEY_EVENT) {
374
	    *result = rc;
375
	} else
376
#endif
377
	    *result = fifo_pull(sp);
378
	returnCode(*result >= KEY_MIN ? KEY_CODE_YES : OK);
379
    }
380
381
    if (win->_use_keypad != sp->_keypad_on)
382
	_nc_keypad(sp, win->_use_keypad);
383
384
    recur_wrefresh(win);
385
386
    if (win->_notimeout || (win->_delay >= 0) || (sp->_cbreak > 1)) {
387
	if (head == -1) {	/* fifo is empty */
388
	    int delay;
389
	    int rc;
390
391
	    TR(TRACE_IEVENT, ("timed delay in wgetch()"));
392
	    if (sp->_cbreak > 1)
393
		delay = (sp->_cbreak - 1) * 100;
394
	    else
395
		delay = win->_delay;
396
397
#ifdef NCURSES_WGETCH_EVENTS
398
	    if (event_delay >= 0 && delay > event_delay)
399
		delay = event_delay;
400
#endif
401
402
	    TR(TRACE_IEVENT, ("delay is %d milliseconds", delay));
403
404
	    rc = check_mouse_activity(sp, delay EVENTLIST_2nd(evl));
405
406
#ifdef NCURSES_WGETCH_EVENTS
407
	    if (rc & 4) {
408
		*result = KEY_EVENT;
409
		returnCode(KEY_CODE_YES);
410
	    }
411
#endif
412
	    if (!rc) {
413
		returnCode(ERR);
414
	    }
415
	}
416
	/* else go on to read data available */
417
    }
418
419
    if (win->_use_keypad) {
420
	/*
421
	 * This is tricky.  We only want to get special-key
422
	 * events one at a time.  But we want to accumulate
423
	 * mouse events until either (a) the mouse logic tells
424
	 * us it's picked up a complete gesture, or (b)
425
	 * there's a detectable time lapse after one.
426
	 *
427
	 * Note: if the mouse code starts failing to compose
428
	 * press/release events into clicks, you should probably
429
	 * increase the wait with mouseinterval().
430
	 */
431
	int runcount = 0;
432
	int rc;
433
434
	do {
435
	    ch = kgetch(sp EVENTLIST_2nd(evl));
436
	    if (ch == KEY_MOUSE) {
437
		++runcount;
438
		if (sp->_mouse_inline(sp))
439
		    break;
440
	    }
441
	    if (sp->_maxclick < 0)
442
		break;
443
	} while
444
	    (ch == KEY_MOUSE
445
	     && (((rc = check_mouse_activity(sp, sp->_maxclick
446
					     EVENTLIST_2nd(evl))) != 0
447
		  && !(rc & 4))
448
		 || !sp->_mouse_parse(sp, runcount)));
449
#ifdef NCURSES_WGETCH_EVENTS
450
	if ((rc & 4) && !ch == KEY_EVENT) {
451
	    _nc_ungetch(sp, ch);
452
	    ch = KEY_EVENT;
453
	}
454
#endif
455
	if (runcount > 0 && ch != KEY_MOUSE) {
456
#ifdef NCURSES_WGETCH_EVENTS
457
	    /* mouse event sequence ended by an event, report event */
458
	    if (ch == KEY_EVENT) {
459
		_nc_ungetch(sp, KEY_MOUSE);	/* FIXME This interrupts a gesture... */
460
	    } else
461
#endif
462
	    {
463
		/* mouse event sequence ended by keystroke, store keystroke */
464
		_nc_ungetch(sp, ch);
465
		ch = KEY_MOUSE;
466
	    }
467
	}
468
    } else {
469
	if (head == -1)
470
	    fifo_push(sp EVENTLIST_2nd(evl));
471
	ch = fifo_pull(sp);
472
    }
473
474
    if (ch == ERR) {
475
#if USE_SIZECHANGE
476
	if (_nc_handle_sigwinch(sp)) {
477
	    _nc_update_screensize(sp);
478
	    /* resizeterm can push KEY_RESIZE */
479
	    if (cooked_key_in_fifo()) {
480
		*result = fifo_pull(sp);
481
		returnCode(*result >= KEY_MIN ? KEY_CODE_YES : OK);
482
	    }
483
	}
484
#endif
485
	returnCode(ERR);
486
    }
487
488
    /*
489
     * If echo() is in effect, display the printable version of the
490
     * key on the screen.  Carriage return and backspace are treated
491
     * specially by Solaris curses:
492
     *
493
     * If carriage return is defined as a function key in the
494
     * terminfo, e.g., kent, then Solaris may return either ^J (or ^M
495
     * if nonl() is set) or KEY_ENTER depending on the echo() mode.
496
     * We echo before translating carriage return based on nonl(),
497
     * since the visual result simply moves the cursor to column 0.
498
     *
499
     * Backspace is a different matter.  Solaris curses does not
500
     * translate it to KEY_BACKSPACE if kbs=^H.  This does not depend
501
     * on the stty modes, but appears to be a hardcoded special case.
502
     * This is a difference from ncurses, which uses the terminfo entry.
503
     * However, we provide the same visual result as Solaris, moving the
504
     * cursor to the left.
505
     */
506
    if (sp->_echo && !(win->_flags & _ISPAD)) {
507
	chtype backup = (ch == KEY_BACKSPACE) ? '\b' : ch;
508
	if (backup < KEY_MIN)
509
	    wechochar(win, backup);
510
    }
511
512
    /*
513
     * Simulate ICRNL mode
514
     */
515
    if ((ch == '\r') && sp->_nl)
516
	ch = '\n';
517
518
    /* Strip 8th-bit if so desired.  We do this only for characters that
519
     * are in the range 128-255, to provide compatibility with terminals
520
     * that display only 7-bit characters.  Note that 'ch' may be a
521
     * function key at this point, so we mustn't strip _those_.
522
     */
523
    if (!use_meta)
524
	if ((ch < KEY_MIN) && (ch & 0x80))
525
	    ch &= 0x7f;
526
527
    T(("wgetch returning : %s", _nc_tracechar(sp, ch)));
528
529
    *result = ch;
530
    returnCode(ch >= KEY_MIN ? KEY_CODE_YES : OK);
531
}
532
533
#ifdef NCURSES_WGETCH_EVENTS
534
NCURSES_EXPORT(int)
535
wgetch_events(WINDOW *win, _nc_eventlist * evl)
536
{
537
    int code;
538
    unsigned long value;
539
540
    T((T_CALLED("wgetch_events(%p,%p)"), win, evl));
541
    code = _nc_wgetch(win,
542
		      &value,
543
		      _nc_use_meta(win)
544
		      EVENTLIST_2nd(evl));
545
    if (code != ERR)
546
	code = value;
547
    returnCode(code);
548
}
549
#endif
550
551
NCURSES_EXPORT(int)
552
wgetch(WINDOW *win)
553
{
554
    int code;
555
    unsigned long value;
556
557
    T((T_CALLED("wgetch(%p)"), win));
558
    code = _nc_wgetch(win,
559
		      &value,
560
		      _nc_use_meta(win)
561
		      EVENTLIST_2nd((_nc_eventlist *) 0));
562
    if (code != ERR)
563
	code = value;
564
    returnCode(code);
565
}
566
567
/*
568
**      int
569
**      kgetch()
570
**
571
**      Get an input character, but take care of keypad sequences, returning
572
**      an appropriate code when one matches the input.  After each character
573
**      is received, set an alarm call based on ESCDELAY.  If no more of the
574
**      sequence is received by the time the alarm goes off, pass through
575
**      the sequence gotten so far.
576
**
577
**	This function must be called when there are no cooked keys in queue.
578
**	(that is head==-1 || peek==head)
579
**
580
*/
581
582
static int
583
kgetch(SCREEN *sp EVENTLIST_2nd(_nc_eventlist * evl))
584
{
585
    TRIES *ptr;
586
    int ch = 0;
587
    int timeleft = GetEscdelay(sp);
588
589
    TR(TRACE_IEVENT, ("kgetch() called"));
590
591
    ptr = sp->_keytry;
592
593
    for (;;) {
594
	if (cooked_key_in_fifo() && sp->_fifo[head] >= KEY_MIN) {
595
	    break;
596
	} else if (!raw_key_in_fifo()) {
597
	    ch = fifo_push(sp EVENTLIST_2nd(evl));
598
	    if (ch == ERR) {
599
		peek = head;	/* the keys stay uninterpreted */
600
		return ERR;
601
	    }
602
#ifdef NCURSES_WGETCH_EVENTS
603
	    else if (ch == KEY_EVENT) {
604
		peek = head;	/* the keys stay uninterpreted */
605
		return fifo_pull(sp);	/* Remove KEY_EVENT from the queue */
606
	    }
607
#endif
608
	}
609
610
	ch = fifo_peek(sp);
611
	if (ch >= KEY_MIN) {
612
	    /* If not first in queue, somebody put this key there on purpose in
613
	     * emergency.  Consider it higher priority than the unfinished
614
	     * keysequence we are parsing.
615
	     */
616
	    peek = head;
617
	    /* assume the key is the last in fifo */
618
	    t_dec();		/* remove the key */
619
	    return ch;
620
	}
621
622
	TR(TRACE_IEVENT, ("ch: %s", _nc_tracechar(sp, (unsigned char) ch)));
623
	while ((ptr != NULL) && (ptr->ch != (unsigned char) ch))
624
	    ptr = ptr->sibling;
625
626
	if (ptr == NULL) {
627
	    TR(TRACE_IEVENT, ("ptr is null"));
628
	    break;
629
	}
630
	TR(TRACE_IEVENT, ("ptr=%p, ch=%d, value=%d",
631
			  ptr, ptr->ch, ptr->value));
632
633
	if (ptr->value != 0) {	/* sequence terminated */
634
	    TR(TRACE_IEVENT, ("end of sequence"));
635
	    if (peek == tail)
636
		fifo_clear(sp);
637
	    else
638
		head = peek;
639
	    return (ptr->value);
640
	}
641
642
	ptr = ptr->child;
643
644
	if (!raw_key_in_fifo()) {
645
	    int rc;
646
647
	    TR(TRACE_IEVENT, ("waiting for rest of sequence"));
648
	    rc = check_mouse_activity(sp, timeleft EVENTLIST_2nd(evl));
649
#ifdef NCURSES_WGETCH_EVENTS
650
	    if (rc & 4) {
651
		TR(TRACE_IEVENT, ("interrupted by a user event"));
652
		/* FIXME Should have preserved remainder timeleft for reuse... */
653
		peek = head;	/* Restart interpreting later */
654
		return KEY_EVENT;
655
	    }
656
#endif
657
	    if (!rc) {
658
		TR(TRACE_IEVENT, ("ran out of time"));
659
		break;
660
	    }
661
	}
662
    }
663
    ch = fifo_pull(sp);
664
    peek = head;
665
    return ch;
666
}