@matheusbsbs / matheus-bagliano / commits / a5e46e6b01
Fork 0

terminal: add a new function to read a single keystroke

Typically, input on the command-line is line-based. It is actually not really easy to get single characters (or better put: keystrokes). We provide two implementations here: - One that handles `/dev/tty` based systems as well as native Windows. The former uses the `tcsetattr()` function to put the terminal into "raw mode", which allows us to read individual keystrokes, one by one. The latter uses `stty.exe` to do the same, falling back to direct Win32 Console access. Thanks to the refactoring leading up to this commit, this is a single function, with the platform-specific details hidden away in conditionally-compiled code blocks. - A fall-back which simply punts and reads back an entire line. Note that the function writes the keystroke into an `strbuf` rather than a `char`, in preparation for reading Escape sequences (e.g. when the user hit an arrow key). This is also required for UTF-8 sequences in case the keystroke corresponds to a non-ASCII letter. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Jan 14, 2020 at 18:43 UTC a5e46e6b0101b960c131dd39b50999cc0e69ed2b
2 files changed +58
compat/terminal.c
+55
@@ -60,6 +60,11 @@ static int disable_echo(void)
60 return disable_bits(ECHO);
61 }
62
63 +static int enable_non_canonical(void)
64 +{
65 + return disable_bits(ICANON | ECHO);
66 +}
67 +
68 #elif defined(GIT_WINDOWS_NATIVE)
69
70 #define INPUT_PATH "CONIN$"
@@ -151,6 +156,10 @@ static int disable_echo(void)
156 return disable_bits(ENABLE_ECHO_INPUT);
157 }
158
159 +static int enable_non_canonical(void)
160 +{
161 + return disable_bits(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
162 +}
163
164 #endif
165
@@ -198,6 +207,33 @@ char *git_terminal_prompt(const char *prompt, int echo)
207 return buf.buf;
208 }
209
210 +int read_key_without_echo(struct strbuf *buf)
211 +{
212 + static int warning_displayed;
213 + int ch;
214 +
215 + if (warning_displayed || enable_non_canonical() < 0) {
216 + if (!warning_displayed) {
217 + warning("reading single keystrokes not supported on "
218 + "this platform; reading line instead");
219 + warning_displayed = 1;
220 + }
221 +
222 + return strbuf_getline(buf, stdin);
223 + }
224 +
225 + strbuf_reset(buf);
226 + ch = getchar();
227 + if (ch == EOF) {
228 + restore_term();
229 + return EOF;
230 + }
231 +
232 + strbuf_addch(buf, ch);
233 + restore_term();
234 + return 0;
235 +}
236 +
237 #else
238
239 char *git_terminal_prompt(const char *prompt, int echo)
@@ -205,4 +241,23 @@ char *git_terminal_prompt(const char *prompt, int echo)
241 return getpass(prompt);
242 }
243
244 +int read_key_without_echo(struct strbuf *buf)
245 +{
246 + static int warning_displayed;
247 + const char *res;
248 +
249 + if (!warning_displayed) {
250 + warning("reading single keystrokes not supported on this "
251 + "platform; reading line instead");
252 + warning_displayed = 1;
253 + }
254 +
255 + res = getpass("");
256 + strbuf_reset(buf);
257 + if (!res)
258 + return EOF;
259 + strbuf_addstr(buf, res);
260 + return 0;
261 +}
262 +
263 #endif
compat/terminal.h
+3
@@ -3,4 +3,7 @@
3
4 char *git_terminal_prompt(const char *prompt, int echo);
5
6 +/* Read a single keystroke, without echoing it to the terminal */
7 +int read_key_without_echo(struct strbuf *buf);
8 +
9 #endif /* COMPAT_TERMINAL_H */