1 /*
2 * Copyright (C) 1984-2025 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>
26 #ifndef ENABLE_EXTENDED_FLAGS
27 #define ENABLE_EXTENDED_FLAGS 0x80
28 #define ENABLE_QUICK_EDIT_MODE 0x40
29 #endif
30 #ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
31 #define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
32 #endif
33 public HANDLE tty;
34 public DWORD init_console_input_mode;
35 public DWORD curr_console_input_mode;
36 public DWORD base_console_input_mode;
37 public DWORD mouse_console_input_mode;
38 #else
39 public int tty;
40 #endif
41 extern int sigs;
42 #if LESSTEST
43 public char *ttyin_name = NULL;
is_lesstest(void)44 public lbool is_lesstest(void)
45 {
46 return ttyin_name != NULL;
47 }
48 #endif /*LESSTEST*/
49
50 #if !MSDOS_COMPILER
open_tty_device(constant char * dev)51 static int open_tty_device(constant char* dev)
52 {
53 #if OS2
54 /* The __open() system call translates "/dev/tty" to "con". */
55 return __open(dev, OPEN_READ);
56 #else
57 return open(dev, OPEN_READ);
58 #endif
59 }
60
61 /*
62 * Open the tty device.
63 * Try ttyname(), then try /dev/tty, then use file descriptor 2.
64 * In Unix, file descriptor 2 is usually attached to the screen,
65 * but also usually lets you read from the keyboard.
66 */
open_tty(void)67 public int open_tty(void)
68 {
69 int fd = -1;
70 #if LESSTEST
71 if (is_lesstest())
72 {
73 fd = open_tty_device(ttyin_name);
74 if (fd < 0)
75 fd = 0; /* assume lesstest uses stdin */
76 }
77 #endif /*LESSTEST*/
78 #if HAVE_TTYNAME
79 if (fd < 0)
80 {
81 constant char *dev = ttyname(2);
82 if (dev != NULL)
83 fd = open_tty_device(dev);
84 }
85 #endif
86 if (fd < 0)
87 fd = open_tty_device("/dev/tty");
88 if (fd < 0)
89 fd = 2;
90 #ifdef __MVS__
91 struct f_cnvrt cvtreq = {SETCVTON, 0, 1047};
92 fcntl(fd, F_CONTROL_CVT, &cvtreq);
93 #endif
94 return fd;
95 }
96 #endif /* MSDOS_COMPILER */
97
98 /*
99 * Open keyboard for input.
100 */
open_getchr(void)101 public void open_getchr(void)
102 {
103 #if MSDOS_COMPILER==WIN32C
104 /* Need this to let child processes inherit our console handle */
105 SECURITY_ATTRIBUTES sa;
106 memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
107 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
108 sa.bInheritHandle = TRUE;
109 tty = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
110 FILE_SHARE_READ, &sa,
111 OPEN_EXISTING, 0L, NULL);
112 GetConsoleMode(tty, &init_console_input_mode);
113 /* base mode: ensure we get ctrl-C events, and don't get VT input. */
114 base_console_input_mode = (init_console_input_mode | ENABLE_PROCESSED_INPUT) & ~ENABLE_VIRTUAL_TERMINAL_INPUT;
115 /* mouse mode: enable mouse and disable quick edit. */
116 mouse_console_input_mode = (base_console_input_mode | ENABLE_MOUSE_INPUT | ENABLE_EXTENDED_FLAGS) & ~ENABLE_QUICK_EDIT_MODE;
117 /* Start with base mode. If --mouse is given, switch to mouse mode in init_mouse. */
118 curr_console_input_mode = base_console_input_mode;
119 SetConsoleMode(tty, curr_console_input_mode);
120 #else
121 #if MSDOS_COMPILER
122 extern int fd0;
123 /*
124 * Open a new handle to CON: in binary mode
125 * for unbuffered keyboard read.
126 */
127 fd0 = dup(0);
128 close(0);
129 tty = open("CON", OPEN_READ);
130 #if MSDOS_COMPILER==DJGPPC
131 /*
132 * Setting stdin to binary causes Ctrl-C to not
133 * raise SIGINT. We must undo that side-effect.
134 */
135 (void) __djgpp_set_ctrl_c(1);
136 #endif
137 #else
138 tty = open_tty();
139 #endif
140 #endif
141 }
142
143 /*
144 * Close the keyboard.
145 */
close_getchr(void)146 public void close_getchr(void)
147 {
148 #if MSDOS_COMPILER==WIN32C
149 SetConsoleMode(tty, init_console_input_mode);
150 CloseHandle(tty);
151 #endif
152 }
153
154 #if MSDOS_COMPILER==WIN32C
155 /*
156 * Close the pipe, restoring the console mode (CMD resets it, losing the mouse).
157 */
pclose(FILE * f)158 public int pclose(FILE *f)
159 {
160 int result;
161
162 result = _pclose(f);
163 SetConsoleMode(tty, curr_console_input_mode);
164 return result;
165 }
166 #endif
167
168 /*
169 * Get the number of lines to scroll when mouse wheel is moved.
170 */
default_wheel_lines(void)171 public int default_wheel_lines(void)
172 {
173 int lines = 1;
174 #if MSDOS_COMPILER==WIN32C
175 if (SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &lines, 0))
176 {
177 if (lines == WHEEL_PAGESCROLL)
178 lines = 3;
179 }
180 #endif
181 return lines;
182 }
183
184 /*
185 * Get a character from the keyboard.
186 */
getchr(void)187 public int getchr(void)
188 {
189 char c;
190 ssize_t result;
191
192 do
193 {
194 flush();
195 #if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
196 /*
197 * In raw read, we don't see ^C so look here for it.
198 */
199 #if MSDOS_COMPILER==WIN32C
200 #if 0
201 if (ABORT_SIGS())
202 return (READ_INTR);
203 #endif
204 c = WIN32getch();
205 #else
206 c = getch();
207 #endif
208 result = 1;
209 if (c == '\003')
210 return (READ_INTR);
211 #else
212 {
213 unsigned char uc;
214 result = iread(tty, &uc, sizeof(char));
215 c = (char) uc;
216 }
217 if (result == READ_INTR)
218 return (READ_INTR);
219 if (result < 0)
220 {
221 /*
222 * Don't call error() here,
223 * because error calls getchr!
224 */
225 quit(QUIT_ERROR);
226 }
227 #endif
228 #if LESSTEST
229 if (c == LESS_DUMP_CHAR)
230 {
231 dump_screen();
232 result = 0;
233 continue;
234 }
235 #endif
236 #if 0 /* allow entering arbitrary hex chars for testing */
237 /* ctrl-A followed by two hex chars makes a byte */
238 {
239 static int hex_in = 0;
240 static int hex_value = 0;
241 if (c == CONTROL('A'))
242 {
243 hex_in = 2;
244 result = 0;
245 continue;
246 }
247 if (hex_in > 0)
248 {
249 int v;
250 if (c >= '0' && c <= '9')
251 v = c - '0';
252 else if (c >= 'a' && c <= 'f')
253 v = c - 'a' + 10;
254 else if (c >= 'A' && c <= 'F')
255 v = c - 'A' + 10;
256 else
257 v = 0;
258 hex_value = (hex_value << 4) | v;
259 if (--hex_in > 0)
260 {
261 result = 0;
262 continue;
263 }
264 c = hex_value;
265 }
266 }
267 #endif
268 /*
269 * Various parts of the program cannot handle
270 * an input character of '\0'.
271 * If a '\0' was actually typed, convert it to '\340' here.
272 */
273 if (c == '\0')
274 c = '\340';
275 } while (result != 1);
276
277 return (c & 0xFF);
278 }
279