UFO: Alien Invasion
Loading...
Searching...
No Matches
unix_console.cpp
Go to the documentation of this file.
1
5
6/*
7Copyright (C) 1997-2001 Id Software, Inc.
8
9This program is free software; you can redistribute it and/or
10modify it under the terms of the GNU General Public License
11as published by the Free Software Foundation; either version 2
12of the License, or (at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18See the GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24*/
25
26#include "../../common/common.h"
27#include "../system.h"
28#include <unistd.h>
29#include <signal.h>
30#include <termios.h>
31#include <fcntl.h>
32#include <sys/time.h>
33
34typedef struct {
35 uint32_t cursor;
36 char buffer[256];
38
39static bool stdinActive;
40/* general flag to tell about tty console mode */
41static bool ttyConsoleActivated = false;
42
43/* some key codes that the terminal may be using, initialised on start up */
44static int TTY_erase;
45static int TTY_eof;
46
47static struct termios TTY_tc;
48
50
51/* This is somewhat of a duplicate of the graphical console history
52 * but it's safer more modular to have our own here */
53#define CON_HISTORY 32
55static int histCurrent = -1, histCount = 0;
56
61static void CON_FlushIn (void)
62{
63 char key;
64 while (read(STDIN_FILENO, &key, 1) != -1)
65 ;
66}
67
74static void Sys_TTYDeleteCharacter (void)
75{
76 char key = '\b';
77 write(STDOUT_FILENO, &key, 1) || 0;
78 key = ' ';
79 write(STDOUT_FILENO, &key, 1) || 0;
80 key = '\b';
81 write(STDOUT_FILENO, &key, 1) || 0;
82}
83
88static void Sys_TTYConsoleHide (void)
89{
90 if (ttyConsoleHistory.cursor > 0) {
91 for (unsigned int i = 0; i < ttyConsoleHistory.cursor; i++)
93 }
94 Sys_TTYDeleteCharacter(); /* Delete "]" */
95}
96
101static void Sys_TTYConsoleShow (void)
102{
103 write(STDOUT_FILENO, "]", 1) || 0;
104 if (ttyConsoleHistory.cursor) {
105 for (unsigned int i = 0; i < ttyConsoleHistory.cursor; i++) {
106 write(STDOUT_FILENO, ttyConsoleHistory.buffer + i, 1) || 0;
107 }
108 }
109}
110
112{
113 const size_t size = lengthof(ttyEditLines);
114
115 assert(histCount <= size);
116 assert(histCount >= 0);
117 assert(histCurrent >= -1);
118 assert(histCurrent <= histCount);
119 /* make some room */
120 for (int i = size - 1; i > 0; i--)
122
123 ttyEditLines[0] = *field;
124 if (histCount < size)
125 histCount++;
126
127 histCurrent = -1; /* re-init */
128}
129
131{
132 assert(histCount <= lengthof(ttyEditLines));
133 assert(histCount >= 0);
134 assert(histCurrent >= -1);
135 assert(histCurrent <= histCount);
136
137 const int histPrev = histCurrent + 1;
138 if (histPrev >= histCount)
139 return nullptr;
140
141 histCurrent++;
142 return &(ttyEditLines[histCurrent]);
143}
144
146{
147 assert(histCount <= CON_HISTORY);
148 assert(histCount >= 0);
149 assert(histCurrent >= -1);
150 assert(histCurrent <= histCount);
151 if (histCurrent >= 0)
152 histCurrent--;
153
154 if (histCurrent == -1)
155 return nullptr;
156
157 return &(ttyEditLines[histCurrent]);
158}
159
164static void Sys_TTYConsoleSigCont (int signum)
165{
167}
168
169void Sys_ShowConsole (bool show)
170{
171 static int ttyConsoleHide = 0;
172
174 return;
175
176 if (show) {
177 assert(ttyConsoleHide > 0);
178 ttyConsoleHide--;
179 if (ttyConsoleHide == 0)
181 } else {
182 if (ttyConsoleHide == 0)
184 ttyConsoleHide++;
185 }
186}
187
193{
195 Sys_TTYDeleteCharacter(); /* Delete "]" */
196 tcsetattr(STDIN_FILENO, TCSADRAIN, &TTY_tc);
197 }
198
199 /* Restore blocking to stdin reads */
200 fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) & ~O_NONBLOCK);
201}
202
204{
205 OBJZERO(*edit);
206}
207
208static bool Sys_IsATTY (void)
209{
210 const char* term = getenv("TERM");
211 return isatty(STDIN_FILENO) && !(term && (Q_streq(term, "raw") || Q_streq(term, "dumb")));
212}
213
218{
219 /* If the process is backgrounded (running non interactively)
220 * then SIGTTIN or SIGTOU is emitted, if not caught, turns into a SIGSTP */
221 signal(SIGTTIN, SIG_IGN);
222 signal(SIGTTOU, SIG_IGN);
223
224 /* If SIGCONT is received, reinitialize console */
225 signal(SIGCONT, Sys_TTYConsoleSigCont);
226
227 /* Make stdin reads non-blocking */
228 fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) | O_NONBLOCK);
229
230 if (!Sys_IsATTY()) {
231 Com_Printf("tty console mode disabled\n");
232 ttyConsoleActivated = false;
233 stdinActive = true;
234 return;
235 }
236
238 tcgetattr(STDIN_FILENO, &TTY_tc);
239 TTY_erase = TTY_tc.c_cc[VERASE];
240 TTY_eof = TTY_tc.c_cc[VEOF];
241 struct termios tc = TTY_tc;
242
243 /*
244 * ECHO: don't echo input characters.
245 * ICANON: enable canonical mode. This enables the special characters EOF, EOL,
246 * EOL2, ERASE, KILL, REPRINT, STATUS, and WERASE, and buffers by lines.
247 * ISIG: when any of the characters INTR, QUIT, SUSP or DSUSP are received,
248 * generate the corresponding sigĀ­nal.
249 */
250 tc.c_lflag &= ~(ECHO | ICANON);
251
252 /*
253 * ISTRIP strip off bit 8
254 * INPCK enable input parity checking
255 */
256 tc.c_iflag &= ~(ISTRIP | INPCK);
257 tc.c_cc[VMIN] = 1;
258 tc.c_cc[VTIME] = 0;
259 tcsetattr(STDIN_FILENO, TCSADRAIN, &tc);
260 ttyConsoleActivated = true;
261}
262
263const char* Sys_ConsoleInput (void)
264{
265 /* we use this when sending back commands */
266 static char text[256];
267
269 char key;
270 int avail = read(STDIN_FILENO, &key, 1);
271 if (avail != -1) {
272 /* we have something
273 * backspace?
274 * NOTE TTimo testing a lot of values .. seems it's the only way to get it to work everywhere */
275 if (key == TTY_erase || key == 127 || key == 8) {
276 if (ttyConsoleHistory.cursor > 0) {
277 ttyConsoleHistory.cursor--;
278 ttyConsoleHistory.buffer[ttyConsoleHistory.cursor] = '\0';
280 }
281 return nullptr;
282 }
283 /* check if this is a control char */
284 if (key && key < ' ') {
285 if (key == '\n') {
286 /* push it in history */
288 Q_strncpyz(text, ttyConsoleHistory.buffer, sizeof(text));
290 key = '\n';
291 write(1, &key, 1) || 0;
292 write(1, "]", 1) || 0;
293 return text;
294 }
295 if (key == '\t') {
296 const size_t size = sizeof(ttyConsoleHistory.buffer);
297 const char* s = ttyConsoleHistory.buffer;
298 char* target = ttyConsoleHistory.buffer;
299 Sys_ShowConsole(false);
300 Com_ConsoleCompleteCommand(s, target, size, &ttyConsoleHistory.cursor, 0);
301 Sys_ShowConsole(true);
302 return nullptr;
303 }
304 avail = read(STDIN_FILENO, &key, 1);
305 if (avail != -1) {
306 /* VT 100 keys */
307 if (key == '[' || key == 'O') {
308 consoleHistory_t* history;
309 avail = read(STDIN_FILENO, &key, 1);
310 if (avail != -1) {
311 switch (key) {
312 case 'A':
314 if (history) {
315 Sys_ShowConsole(false);
316 ttyConsoleHistory = *history;
317 Sys_ShowConsole(true);
318 }
319 CON_FlushIn();
320 return nullptr;
321 break;
322 case 'B':
323 history = Sys_TTYConsoleHistoryNext();
324 Sys_ShowConsole(false);
325 if (history) {
326 ttyConsoleHistory = *history;
327 } else {
329 }
330 Sys_ShowConsole(true);
331 CON_FlushIn();
332 return nullptr;
333 break;
334 case 'C':
335 return nullptr;
336 case 'D':
337 return nullptr;
338 }
339 }
340 }
341 }
342 CON_FlushIn();
343 return nullptr;
344 }
345 if (ttyConsoleHistory.cursor >= sizeof(text) - 1)
346 return nullptr;
347 /* push regular character */
348 ttyConsoleHistory.buffer[ttyConsoleHistory.cursor] = key;
349 ttyConsoleHistory.cursor++;
350 /* print the current line (this is differential) */
351 write(STDOUT_FILENO, &key, 1) || 0;
352 }
353
354 return nullptr;
355 } else if (stdinActive) {
356 fd_set fdset;
357 struct timeval timeout;
358
359 FD_ZERO(&fdset);
360 FD_SET(STDIN_FILENO, &fdset); /* stdin */
361 timeout.tv_sec = 0;
362 timeout.tv_usec = 0;
363 if (select(STDIN_FILENO + 1, &fdset, nullptr, nullptr, &timeout) == -1
364 || !FD_ISSET(STDIN_FILENO, &fdset))
365 return nullptr;
366
367 const int len = read(STDIN_FILENO, text, sizeof(text));
368 if (len == 0) { /* eof! */
369 stdinActive = false;
370 return nullptr;
371 }
372
373 if (len < 1)
374 return nullptr;
375 text[len - 1] = 0; /* rip off the /n and terminate */
376
377 return text;
378 }
379 return nullptr;
380}
381
382void Sys_ConsoleOutput (const char* string)
383{
384 /* BUG: for some reason, NDELAY also affects stdout (1) when used on stdin (0). */
385 const int origflags = fcntl(STDOUT_FILENO, F_GETFL, 0);
386
387 fcntl(STDOUT_FILENO, F_SETFL, origflags & ~FNDELAY);
388 while (*string) {
389 const ssize_t written = write(STDOUT_FILENO, string, strlen(string));
390 if (written <= 0)
391 break; /* sorry, I cannot do anything about this error - without an output */
392 string += written;
393 }
394 fcntl(STDOUT_FILENO, F_SETFL, origflags);
395}
unsigned int key
Definition cl_input.cpp:64
bool Com_ConsoleCompleteCommand(const char *s, char *target, size_t bufSize, uint32_t *pos, uint32_t offset)
Console completion for command and variables.
Definition common.cpp:734
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
definitions common between client and server, but not game lib
voidpf void uLong size
Definition ioapi.h:42
QGL_EXTERN GLuint GLchar GLuint * len
Definition r_gl.h:99
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLenum field
Definition r_gl.h:101
#define Q_streq(a, b)
Definition shared.h:136
#define OBJZERO(obj)
Definition shared.h:178
#define lengthof(x)
Definition shared.h:105
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition shared.cpp:457
System specific stuff.
static consoleHistory_t ttyEditLines[CON_HISTORY]
void Sys_ConsoleInit(void)
Initialize the console input (tty mode if possible).
static int TTY_erase
static void Sys_TTYConsoleHistoryClear(consoleHistory_t *edit)
static int histCurrent
static consoleHistory_t * Sys_TTYConsoleHistoryPrevious(void)
static int histCount
const char * Sys_ConsoleInput(void)
static bool Sys_IsATTY(void)
static void Sys_TTYConsoleHide(void)
Clear the display of the line currently edited bring cursor back to beginning of line.
void Sys_ShowConsole(bool show)
static struct termios TTY_tc
static int TTY_eof
void Sys_ConsoleShutdown(void)
Shutdown the console.
static void Sys_TTYDeleteCharacter(void)
Output a backspace.
static consoleHistory_t * Sys_TTYConsoleHistoryNext(void)
void Sys_ConsoleOutput(const char *string)
static void CON_FlushIn(void)
Flush stdin, I suspect some terminals are sending a LOT of shit.
static consoleHistory_t ttyConsoleHistory
#define CON_HISTORY
static void Sys_TTYConsoleSigCont(int signum)
Reinitialize console input after receiving SIGCONT, as on Linux the terminal seems to lose all set at...
static void Sys_TTYConsoleHistoryAdd(consoleHistory_t *field)
static bool ttyConsoleActivated
static bool stdinActive
static void Sys_TTYConsoleShow(void)
Show the current line.