1/*
2 * Copyright (C) 1984-2023  Mark Nudelman
3 *
4 * You may distribute under the terms of either the GNU General Public
5 * License or the Less License, as specified in the README file.
6 *
7 * For more information, see the README file.
8 */
9
10
11/*
12 * Routines dealing with getting input from the keyboard (i.e. from the user).
13 */
14
15#include "less.h"
16#if OS2
17#include "cmd.h"
18#include "pckeys.h"
19#endif
20#if MSDOS_COMPILER==WIN32C
21#define WIN32_LEAN_AND_MEAN
22#ifndef _WIN32_WINNT
23#define _WIN32_WINNT 0x400
24#endif
25#include <windows.h>
26public DWORD console_mode;
27public HANDLE tty;
28#else
29public int tty;
30#endif
31#if LESSTEST
32public char *ttyin_name = NULL;
33#endif /*LESSTEST*/
34extern int sigs;
35extern int utf_mode;
36extern int wheel_lines;
37
38#if !MSDOS_COMPILER
39static int open_tty_device(constant char* dev)
40{
41#if OS2
42	/* The __open() system call translates "/dev/tty" to "con". */
43	return __open(dev, OPEN_READ);
44#else
45	return open(dev, OPEN_READ);
46#endif
47}
48
49/*
50 * Open the tty device.
51 * Try ttyname(), then try /dev/tty, then use file descriptor 2.
52 * In Unix, file descriptor 2 is usually attached to the screen,
53 * but also usually lets you read from the keyboard.
54 */
55public int open_tty(void)
56{
57	int fd = -1;
58#if LESSTEST
59	if (ttyin_name != NULL)
60		fd = open_tty_device(ttyin_name);
61#endif /*LESSTEST*/
62#if HAVE_TTYNAME
63	if (fd < 0)
64	{
65		constant char *dev = ttyname(2);
66		if (dev != NULL)
67			fd = open_tty_device(dev);
68	}
69#endif
70	if (fd < 0)
71		fd = open_tty_device("/dev/tty");
72	if (fd < 0)
73		fd = 2;
74	return fd;
75}
76#endif /* MSDOS_COMPILER */
77
78/*
79 * Open keyboard for input.
80 */
81public void open_getchr(void)
82{
83#if MSDOS_COMPILER==WIN32C
84	/* Need this to let child processes inherit our console handle */
85	SECURITY_ATTRIBUTES sa;
86	memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
87	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
88	sa.bInheritHandle = TRUE;
89	tty = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
90			FILE_SHARE_READ, &sa,
91			OPEN_EXISTING, 0L, NULL);
92	GetConsoleMode(tty, &console_mode);
93	/* Make sure we get Ctrl+C events. */
94	SetConsoleMode(tty, ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT);
95#else
96#if MSDOS_COMPILER
97	extern int fd0;
98	/*
99	 * Open a new handle to CON: in binary mode
100	 * for unbuffered keyboard read.
101	 */
102	 fd0 = dup(0);
103	 close(0);
104	 tty = open("CON", OPEN_READ);
105#if MSDOS_COMPILER==DJGPPC
106	/*
107	 * Setting stdin to binary causes Ctrl-C to not
108	 * raise SIGINT.  We must undo that side-effect.
109	 */
110	(void) __djgpp_set_ctrl_c(1);
111#endif
112#else
113	tty = open_tty();
114#endif
115#endif
116}
117
118/*
119 * Close the keyboard.
120 */
121public void close_getchr(void)
122{
123#if MSDOS_COMPILER==WIN32C
124	SetConsoleMode(tty, console_mode);
125	CloseHandle(tty);
126#endif
127}
128
129#if MSDOS_COMPILER==WIN32C
130/*
131 * Close the pipe, restoring the keyboard (CMD resets it, losing the mouse).
132 */
133public int pclose(FILE *f)
134{
135	int result;
136
137	result = _pclose(f);
138	SetConsoleMode(tty, ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT);
139	return result;
140}
141#endif
142
143/*
144 * Get the number of lines to scroll when mouse wheel is moved.
145 */
146public int default_wheel_lines(void)
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/*
160 * Get a character from the keyboard.
161 */
162public int getchr(void)
163{
164	char c;
165	int result;
166
167	do
168	{
169		flush();
170#if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
171		/*
172		 * In raw read, we don't see ^C so look here for it.
173		 */
174#if MSDOS_COMPILER==WIN32C
175		if (ABORT_SIGS())
176			return (READ_INTR);
177		c = WIN32getch();
178#else
179		c = getch();
180#endif
181		result = 1;
182		if (c == '\003')
183			return (READ_INTR);
184#else
185		{
186			unsigned char uc;
187			result = iread(tty, &uc, sizeof(char));
188			c = (char) uc;
189		}
190		if (result == READ_INTR)
191			return (READ_INTR);
192		if (result < 0)
193		{
194			/*
195			 * Don't call error() here,
196			 * because error calls getchr!
197			 */
198			quit(QUIT_ERROR);
199		}
200#endif
201#if LESSTEST
202		if (c == LESS_DUMP_CHAR)
203		{
204			dump_screen();
205			result = 0;
206			continue;
207		}
208#endif
209#if 0 /* allow entering arbitrary hex chars for testing */
210		/* ctrl-A followed by two hex chars makes a byte */
211	{
212		static int hex_in = 0;
213		static int hex_value = 0;
214		if (c == CONTROL('A'))
215		{
216			hex_in = 2;
217			result = 0;
218			continue;
219		}
220		if (hex_in > 0)
221		{
222			int v;
223			if (c >= '0' && c <= '9')
224				v = c - '0';
225			else if (c >= 'a' && c <= 'f')
226				v = c - 'a' + 10;
227			else if (c >= 'A' && c <= 'F')
228				v = c - 'A' + 10;
229			else
230				v = 0;
231			hex_value = (hex_value << 4) | v;
232			if (--hex_in > 0)
233			{
234				result = 0;
235				continue;
236			}
237			c = hex_value;
238		}
239	}
240#endif
241		/*
242		 * Various parts of the program cannot handle
243		 * an input character of '\0'.
244		 * If a '\0' was actually typed, convert it to '\340' here.
245		 */
246		if (c == '\0')
247			c = '\340';
248	} while (result != 1);
249
250	return (c & 0xFF);
251}
252