1106356Smarcel/*
2106356Smarcel * Copyright (C) 1984-2021  Mark Nudelman
3106356Smarcel *
4106356Smarcel * You may distribute under the terms of either the GNU General Public
5106356Smarcel * License or the Less License, as specified in the README file.
6106356Smarcel *
7106356Smarcel * For more information, see the README file.
8106356Smarcel */
9106356Smarcel
10106356Smarcel
11106356Smarcel/*
12106356Smarcel * Routines dealing with getting input from the keyboard (i.e. from the user).
13106356Smarcel */
14106356Smarcel
15106356Smarcel#include "less.h"
16106356Smarcel#if OS2
17106356Smarcel#include "cmd.h"
18106356Smarcel#include "pckeys.h"
19106356Smarcel#endif
20106356Smarcel#if MSDOS_COMPILER==WIN32C
21106356Smarcel#define WIN32_LEAN_AND_MEAN
22106356Smarcel#ifndef _WIN32_WINNT
23106356Smarcel#define _WIN32_WINNT 0x400
24106356Smarcel#endif
25106356Smarcel#include <windows.h>
26106722Smarcelpublic DWORD console_mode;
27106356Smarcelpublic HANDLE tty;
28106356Smarcel#else
29106722Smarcelpublic int tty;
30106722Smarcel#endif
31106356Smarcel#if LESSTEST
32106722Smarcelpublic char *ttyin_name = NULL;
33106356Smarcelpublic int rstat_file = -1;
34106356Smarcel#endif /*LESSTEST*/
35106356Smarcelextern int sigs;
36106722Smarcelextern int utf_mode;
37106722Smarcelextern int wheel_lines;
38106356Smarcel
39106356Smarcel/*
40251812Shrs * Get name of tty device.
41106722Smarcel */
42106722Smarcel#if !MSDOS_COMPILER
43106722Smarcel	public char *
44106722Smarceltty_device(VOID_PARAM)
45106356Smarcel{
46106722Smarcel	char *dev = NULL;
47118393Sru#if HAVE_TTYNAME
48106722Smarcel	dev = ttyname(2);
49208622Smarcel#endif
50118180Sru	if (dev == NULL)
51222726Smarcel		dev = "/dev/tty";
52222726Smarcel#if LESSTEST
53118180Sru	if (ttyin_name != NULL)
54118393Sru		dev = ttyin_name;
55118180Sru#endif /*LESSTEST*/
56118180Sru	return dev;
57118180Sru}
58222726Smarcel#endif /* MSDOS_COMPILER */
59222726Smarcel
60222726Smarcel/*
61118180Sru * Open keyboard for input.
62118180Sru */
63222726Smarcel	public void
64222726Smarcelopen_getchr(VOID_PARAM)
65222726Smarcel{
66254075Smarcel#if MSDOS_COMPILER==WIN32C
67118180Sru	/* Need this to let child processes inherit our console handle */
68228043Smarcel	SECURITY_ATTRIBUTES sa;
69228043Smarcel	memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
70118180Sru	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
71254075Smarcel	sa.bInheritHandle = TRUE;
72254075Smarcel	tty = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
73118180Sru			FILE_SHARE_READ, &sa,
74118180Sru			OPEN_EXISTING, 0L, NULL);
75222726Smarcel	GetConsoleMode(tty, &console_mode);
76133426Smarcel	/* Make sure we get Ctrl+C events. */
77133426Smarcel	SetConsoleMode(tty, ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT);
78106722Smarcel#else
79106722Smarcel#if MSDOS_COMPILER
80251812Shrs	extern int fd0;
81222766Smarcel	/*
82251812Shrs	 * Open a new handle to CON: in binary mode
83222766Smarcel	 * for unbuffered keyboard read.
84222726Smarcel	 */
85106722Smarcel	 fd0 = dup(0);
86	 close(0);
87	 tty = open("CON", OPEN_READ);
88#if MSDOS_COMPILER==DJGPPC
89	/*
90	 * Setting stdin to binary causes Ctrl-C to not
91	 * raise SIGINT.  We must undo that side-effect.
92	 */
93	(void) __djgpp_set_ctrl_c(1);
94#endif
95#else
96	/*
97	 * Try /dev/tty.
98	 * If that doesn't work, use file descriptor 2,
99	 * which in Unix is usually attached to the screen,
100	 * but also usually lets you read from the keyboard.
101	 */
102#if OS2
103	/* The __open() system call translates "/dev/tty" to "con". */
104	tty = __open(tty_device(), OPEN_READ);
105#else
106	tty = open(tty_device(), OPEN_READ);
107#endif
108	if (tty < 0)
109		tty = 2;
110#endif
111#endif
112}
113
114/*
115 * Close the keyboard.
116 */
117	public void
118close_getchr(VOID_PARAM)
119{
120#if MSDOS_COMPILER==WIN32C
121	SetConsoleMode(tty, console_mode);
122	CloseHandle(tty);
123#endif
124}
125
126#if MSDOS_COMPILER==WIN32C
127/*
128 * Close the pipe, restoring the keyboard (CMD resets it, losing the mouse).
129 */
130	int
131pclose(f)
132	FILE *f;
133{
134	int result;
135
136	result = _pclose(f);
137	SetConsoleMode(tty, ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT);
138	return result;
139}
140#endif
141
142/*
143 * Get the number of lines to scroll when mouse wheel is moved.
144 */
145	public int
146default_wheel_lines(VOID_PARAM)
147{
148	int lines = 1;
149#if MSDOS_COMPILER==WIN32C
150	if (SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &lines, 0))
151	{
152		if (lines == WHEEL_PAGESCROLL)
153			lines = 3;
154	}
155#endif
156	return lines;
157}
158
159#if LESSTEST
160	public void
161rstat(st)
162	char st;
163{
164	if (rstat_file < 0)
165		return;
166	lseek(rstat_file, SEEK_SET, 0);
167	write(rstat_file, &st, 1);
168}
169#endif /*LESSTEST*/
170
171/*
172 * Get a character from the keyboard.
173 */
174	public int
175getchr(VOID_PARAM)
176{
177	char c;
178	int result;
179
180	do
181	{
182		flush();
183#if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
184		/*
185		 * In raw read, we don't see ^C so look here for it.
186		 */
187#if MSDOS_COMPILER==WIN32C
188		if (ABORT_SIGS())
189			return (READ_INTR);
190		c = WIN32getch();
191#else
192		c = getch();
193#endif
194		result = 1;
195		if (c == '\003')
196			return (READ_INTR);
197#else
198#if LESSTEST
199		rstat('R');
200#endif /*LESSTEST*/
201		{
202			unsigned char uc;
203			result = iread(tty, &uc, sizeof(char));
204			c = (char) uc;
205		}
206#if LESSTEST
207		rstat('B');
208#endif /*LESSTEST*/
209		if (result == READ_INTR)
210			return (READ_INTR);
211		if (result < 0)
212		{
213			/*
214			 * Don't call error() here,
215			 * because error calls getchr!
216			 */
217			quit(QUIT_ERROR);
218		}
219#endif
220#if 0 /* allow entering arbitrary hex chars for testing */
221		/* ctrl-A followed by two hex chars makes a byte */
222	{
223		static int hex_in = 0;
224		static int hex_value = 0;
225		if (c == CONTROL('A'))
226		{
227			hex_in = 2;
228			result = 0;
229			continue;
230		}
231		if (hex_in > 0)
232		{
233			int v;
234			if (c >= '0' && c <= '9')
235				v = c - '0';
236			else if (c >= 'a' && c <= 'f')
237				v = c - 'a' + 10;
238			else if (c >= 'A' && c <= 'F')
239				v = c - 'A' + 10;
240			else
241				v = 0;
242			hex_value = (hex_value << 4) | v;
243			if (--hex_in > 0)
244			{
245				result = 0;
246				continue;
247			}
248			c = hex_value;
249		}
250	}
251#endif
252		/*
253		 * Various parts of the program cannot handle
254		 * an input character of '\0'.
255		 * If a '\0' was actually typed, convert it to '\340' here.
256		 */
257		if (c == '\0')
258			c = '\340';
259	} while (result != 1);
260
261	return (c & 0xFF);
262}
263