UFO: Alien Invasion
Loading...
Searching...
No Matches
cl_console.cpp
Go to the documentation of this file.
1
5
6/*
7All original material Copyright (C) 2002-2025 UFO: Alien Invasion.
8
9Original file from Quake 2 v3.21: quake2-2.31/client/console.c
10Copyright (C) 1997-2001 Id Software, Inc.
11
12This program is free software; you can redistribute it and/or
13modify it under the terms of the GNU General Public License
14as published by the Free Software Foundation; either version 2
15of the License, or (at your option) any later version.
16
17This program is distributed in the hope that it will be useful,
18but WITHOUT ANY WARRANTY; without even the implied warranty of
19MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20
21See the GNU General Public License for more details.
22
23You should have received a copy of the GNU General Public License
24along with this program; if not, write to the Free Software
25Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26
27*/
28
29#include "cl_console.h"
30#include "client.h"
31#include "cgame/cl_game.h"
32#include "input/cl_keys.h"
33#include "renderer/r_draw.h"
34#include "../shared/utf8.h"
35
36#define ColorIndex(c) (((c) - '0') & 0x07)
37
38/* set ABGR color values */
39static const uint32_t g_color_table[] =
40{
41 0xFF000000,
42 0xFF0000FF,
43 0xFF00FF00,
44 0xFF00FFFF,
45 0xFFFF0000,
46 0xFFFFFF00,
47 0xFFFF00FF,
48 0xFFFFFFFF
49};
50
51#define CONSOLE_CHAR_ALIGN 4
52#define NUM_CON_TIMES 8
53#define CON_TEXTSIZE 32768
54#define CONSOLE_CURSOR_CHAR 11
55#define CONSOLE_HISTORY_FILENAME "history"
56
57typedef struct {
59
62 int pos;
64
67
69} console_t;
70
75const int con_fontHeight = 12;
76const int con_fontWidth = 10;
77const int con_fontShift = 3;
78
79static void Con_Clear (void)
80{
81 const size_t size = lengthof(con.text);
82
83 for (unsigned int i = 0; i < size; i++)
84 con.text[i] = (CON_COLOR_WHITE << 8) | ' ';
85}
86
92static void Con_DrawText (const short* text, int x, int y, size_t width)
93{
94 for (unsigned int xPos = 0; xPos < width; xPos++) {
95 const int currentColor = (text[xPos] >> 8) & 7;
96 R_DrawChar(x + ((xPos + 1) << con_fontShift), y, text[xPos], g_color_table[currentColor]);
97 }
98}
99
105void Con_DrawString (const char* txt, int x, int y, unsigned int width)
106{
107 short buf[512];
108 const size_t size = lengthof(buf);
109 char c;
110
111 if (width > size || strlen(txt) > size)
112 Sys_Error("Overflow in Con_DrawString");
113
114 int color = CON_COLOR_WHITE;
115 short* pos = buf;
116
117 while ((c = *txt) != 0) {
118 if (Q_IsColorString(txt) ) {
119 color = ColorIndex(*(txt + 1));
120 txt += 2;
121 continue;
122 }
123
124 *pos = (color << 8) | c;
125
126 txt++;
127 pos++;
128 }
129 Con_DrawText(buf, x, y, width);
130}
131
132static void Key_ClearTyping (void)
133{
134 keyLines[editLine][1] = 0; /* clear any typing */
135 keyLinePos = 1;
136}
137
139{
141
142 if (cls.keyDest == key_console) {
144 } else {
146 }
147}
148
149static void Con_ToggleChat_f (void)
150{
152
153 if (cls.keyDest == key_console) {
154 if (cls.state == ca_active)
156 } else
158}
159
163static void Con_Clear_f (void)
164{
165 Con_Clear();
166}
167
172void Con_Scroll (int scroll)
173{
174 con.displayLine += scroll;
175 if (con.displayLine > con.currentLine)
176 con.displayLine = con.currentLine;
177 else if (con.displayLine < 0)
178 con.displayLine = 0;
179}
180
185{
186 short tbuf[CON_TEXTSIZE];
187 const int width = (viddef.context.width >> con_fontShift);
188
189 if (width < 1 || width == con.lineWidth)
190 return;
191
192 int oldWidth = con.lineWidth;
193 con.lineWidth = width;
194 int oldTotalLines = con.totalLines;
195 con.totalLines = CON_TEXTSIZE / con.lineWidth;
196 int numLines = oldTotalLines;
197
198 if (con.totalLines < numLines)
199 numLines = con.totalLines;
200
201 int numChars = oldWidth;
202
203 if (con.lineWidth < numChars)
204 numChars = con.lineWidth;
205
206 memcpy(tbuf, con.text, sizeof(tbuf));
207 Con_Clear();
208
209 for (int i = 0; i < numLines; i++) {
210 for (int j = 0; j < numChars; j++) {
211 con.text[(con.totalLines - 1 - i) * con.lineWidth + j] = tbuf[((con.currentLine - i + oldTotalLines) % oldTotalLines) * oldWidth + j];
212 }
213 }
214
215 con.currentLine = con.totalLines - 1;
216 con.displayLine = con.currentLine;
217}
218
224{
225 char line[MAXCMDLINE];
226
227 if (!con_history->integer)
228 return;
229
232 if (!f)
233 return;
234
235 /* we have to skip the initial line char and the string end char. */
236 while (fgets(line, MAXCMDLINE - 2, f.getFile())) {
237 if (line[strlen(line) - 1] == '\n')
238 line[strlen(line) - 1] = 0;
239 Q_strncpyz(&keyLines[editLine][1], line, MAXCMDLINE - 1);
240 editLine = (editLine + 1) % MAXKEYLINES;
242 keyLines[editLine][1] = 0;
243 }
244}
245
251{
252 /* maybe con_history is not initialized here (early Sys_Error) */
253 if (!con_history || !con_history->integer)
254 return;
255
258 if (!f.file()) {
259 Com_Printf("Can not open " CONSOLE_HISTORY_FILENAME " for writing\n");
260 return;
261 }
262
263 const char* lastLine = nullptr;
264 for (int i = 0; i < historyLine; i++) {
265 if (lastLine && !strncmp(lastLine, &(keyLines[i][1]), MAXCMDLINE - 1))
266 continue;
267
268 lastLine = &(keyLines[i][1]);
269 if (*lastLine) {
270 FS_Write(lastLine, strlen(lastLine), &f);
271 FS_Write("\n", 1, &f);
272 }
273 }
274}
275
276void Con_Init (void)
277{
278 Com_Printf("\n----- console initialization -------\n");
279
280 /* register our commands and cvars */
281 con_notifytime = Cvar_Get("con_notifytime", "10", CVAR_ARCHIVE, "How many seconds console messages should be shown before they fade away");
282 con_history = Cvar_Get("con_history", "1", CVAR_ARCHIVE, "Permanent console history");
283 con_background = Cvar_Get("con_background", "1", CVAR_ARCHIVE, "Console is rendered with background image");
284
285 Cmd_AddCommand("toggleconsole", Con_ToggleConsole_f, N_("Show/hide ufoconsole."));
286 Cmd_AddCommand("togglechat", Con_ToggleChat_f);
287 Cmd_AddCommand("clear", Con_Clear_f, "Clear console text");
288
289 /* load console history if con_history is true */
291
292 OBJZERO(con);
293 con.lineWidth = VID_NORM_WIDTH / con_fontWidth;
294 con.totalLines = lengthof(con.text) / con.lineWidth;
295 con.initialized = true;
296
297 Com_Printf("Console initialized.\n");
298}
299
300
301static void Con_Linefeed (void)
302{
303 con.pos = 0;
304 if (con.displayLine == con.currentLine)
305 con.displayLine++;
306 con.currentLine++;
307
308 for (int i = 0; i < con.lineWidth; i++)
309 con.text[(con.currentLine % con.totalLines) * con.lineWidth + i] = (CON_COLOR_WHITE << 8) | ' ';
310}
311
318void Con_Print (const char* txt)
319{
320 int y;
321 int c;
322 static bool cr;
323
324 if (!con.initialized)
325 return;
326
327 int color = CON_COLOR_WHITE;
328
329 while ((c = *txt) != 0) {
330 const int charLength = UTF8_char_len(c);
331 if (Q_IsColorString(txt) ) {
332 color = ColorIndex(*(txt + 1));
333 txt += 2;
334 continue;
335 }
336
337 /* count word length */
338 int len;
339 for (len = 0; len < con.lineWidth; len++)
340 if (txt[len] <= ' ')
341 break;
342
343 /* word wrap */
344 if (len != con.lineWidth && (con.pos + len > con.lineWidth))
345 con.pos = 0;
346
347 txt += charLength;
348
349 if (cr) {
350 con.currentLine--;
351 cr = false;
352 }
353
354 if (!con.pos) {
355 Con_Linefeed();
356 }
357
358 if (charLength > 1)
359 c = '.';
360
361 switch (c) {
362 case '\n':
363 con.pos = 0;
364 color = CON_COLOR_WHITE;
365 break;
366
367 case '\r':
368 con.pos = 0;
369 color = CON_COLOR_WHITE;
370 cr = true;
371 break;
372
373 default: /* display character and advance */
374 y = con.currentLine % con.totalLines;
375 con.text[y * con.lineWidth + con.pos] = (color << 8) | c;
376 con.pos++;
377 if (con.pos >= con.lineWidth)
378 con.pos = 0;
379 break;
380 }
381 }
382}
383
384/*
385==============================================================================
386DRAWING
387==============================================================================
388*/
389
393void Con_Close (void)
394{
395 if (cls.keyDest == key_console)
397}
398
402static void Con_DrawInput (void)
403{
404 short editlinecopy[MAXCMDLINE];
405 const size_t size = lengthof(editlinecopy);
406
407 if (cls.keyDest != key_console && cls.state == ca_active)
408 return; /* don't draw anything (always draw if not active) */
409
410 int y = 0;
411 for (unsigned int i = 0; i < size; i++) {
412 editlinecopy[i] = (CON_COLOR_WHITE << 8) | keyLines[editLine][i];
413 if (keyLines[editLine][i] == '\0')
414 break;
415 y++;
416 }
417 short* text = editlinecopy;
418
419 /* add the cursor frame */
420 if ((int)(CL_Milliseconds() >> 8) & 1) {
422 if (keyLinePos == y)
423 y++;
424 }
425
426 /* fill out remainder with spaces */
427 for (unsigned int i = y; i < size; i++)
428 text[i] = (CON_COLOR_WHITE << 8) | ' ';
429
430 /* prestep if horizontally scrolling */
431 if (keyLinePos >= con.lineWidth)
432 text += 1 + keyLinePos - con.lineWidth;
433
434 /* draw it */
435 y = con.visLines - con_fontHeight;
436
437 Con_DrawText(text, 0, y - CONSOLE_CHAR_ALIGN, con.lineWidth);
438}
439
443void Con_DrawConsole (float frac)
444{
445 unsigned int lines = viddef.context.height * frac;
446 if (lines == 0)
447 return;
448
449 if (lines > viddef.context.height)
450 lines = viddef.context.height;
451
452 /* draw the background */
453 if (con_background->integer)
454 R_DrawStretchImage(0, viddef.virtualHeight * (frac - 1) , viddef.virtualWidth, viddef.virtualHeight, R_FindImage("pics/background/conback", it_pic));
455
456 char consoleMessage[128];
457 Com_sprintf(consoleMessage, sizeof(consoleMessage), "Hit esc to close - v%s", UFO_VERSION);
458 {
459 const int len = strlen(consoleMessage);
460 const int versionX = viddef.context.width - (len * con_fontWidth) - CONSOLE_CHAR_ALIGN;
461 const int versionY = lines - (con_fontHeight + CONSOLE_CHAR_ALIGN);
462 const uint32_t color = g_color_table[CON_COLOR_WHITE];
463
464 for (int x = 0; x < len; x++)
465 R_DrawChar(versionX + x * con_fontWidth, versionY, consoleMessage[x], color);
466 }
467
468 /* draw the text */
469 con.visLines = lines;
470
471 int rows = (lines - con_fontHeight * 2) >> con_fontShift; /* rows of text to draw */
472
473 int y = lines - con_fontHeight * 3;
474
475 /* draw from the bottom up */
476 if (con.displayLine != con.currentLine) {
477 const uint32_t color = g_color_table[CON_COLOR_GREEN];
478 /* draw arrows to show the buffer is backscrolled */
479 for (int x = 0; x < con.lineWidth; x += 4)
480 R_DrawChar((x + 1) << con_fontShift, y, '^', color);
481
482 y -= con_fontHeight;
483 rows--;
484 }
485
486 for (int i = 0, row = con.displayLine; i < rows; i++, y -= con_fontHeight, row--) {
487 if (row < 0)
488 break;
489 if (con.currentLine - row >= con.totalLines)
490 break; /* past scrollback wrap point */
491
492 short* text = con.text + (row % con.totalLines) * con.lineWidth;
493
494 Con_DrawText(text, 0, y, con.lineWidth);
495 }
496
497 /* draw the input prompt, user text, and cursor if desired */
499}
void Con_Init(void)
static void Con_ToggleChat_f(void)
void Con_LoadConsoleHistory(void)
Load the console history.
#define CONSOLE_CURSOR_CHAR
void Con_SaveConsoleHistory(void)
Stores the console history.
const int con_fontShift
#define CONSOLE_HISTORY_FILENAME
static cvar_t * con_history
void Con_Print(const char *txt)
Handles cursor positioning, line wrapping, etc All console printing must go through this in order to ...
static void Key_ClearTyping(void)
static void Con_DrawText(const short *text, int x, int y, size_t width)
static void Con_Clear(void)
void Con_Close(void)
Hide the gameconsole if active.
#define ColorIndex(c)
static const uint32_t g_color_table[]
#define CON_TEXTSIZE
const int con_fontWidth
const int con_fontHeight
void Con_DrawConsole(float frac)
Draws the console with the solid background.
#define CONSOLE_CHAR_ALIGN
static console_t con
static cvar_t * con_background
void Con_Scroll(int scroll)
Scrolls the console.
void Con_ToggleConsole_f(void)
void Con_CheckResize(void)
If the line width has changed, reformat the buffer.
static void Con_Linefeed(void)
static void Con_Clear_f(void)
Clears the console buffer.
void Con_DrawString(const char *txt, int x, int y, unsigned int width)
static cvar_t * con_notifytime
static void Con_DrawInput(void)
The input line scrolls horizontally if typing goes beyond the right edge.
Console header file.
Shared game type headers.
char keyLines[MAXKEYLINES][MAXCMDLINE]
Definition cl_keys.cpp:37
int editLine
Definition cl_keys.cpp:42
uint32_t keyLinePos
Definition cl_keys.cpp:38
void Key_SetDest(keydest_t keyDest)
Sets the keyDest in cls.
Definition cl_keys.cpp:815
int historyLine
Definition cl_keys.cpp:43
Header file for keyboard handler.
@ key_game
Definition cl_keys.h:182
@ key_console
Definition cl_keys.h:183
#define MAXKEYLINES
Definition cl_keys.h:186
client_static_t cls
Definition cl_main.cpp:83
int CL_Milliseconds(void)
Definition cl_main.cpp:1207
#define VID_NORM_WIDTH
Definition cl_renderer.h:40
#define N_(String)
Definition cl_shared.h:46
@ ca_active
Definition cl_shared.h:80
viddef_t viddef
Definition cl_video.cpp:34
Primary header for client.
void Cmd_AddCommand(const char *cmdName, xcommand_t function, const char *desc)
Add a new command to the script interface.
Definition cmd.cpp:744
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
#define Q_IsColorString(p)
Definition common.h:215
#define CON_COLOR_WHITE
Definition common.h:233
#define CON_COLOR_GREEN
Definition common.h:228
#define MAXCMDLINE
Definition common.h:285
#define UFO_VERSION
Definition common.h:36
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags, const char *desc)
Init or return a cvar.
Definition cvar.cpp:342
#define CVAR_ARCHIVE
Definition cvar.h:40
int FS_Write(const void *buffer, int len, qFILE *f)
Properly handles partial writes.
Definition files.cpp:1513
int FS_OpenFile(const char *filename, qFILE *file, filemode_t mode)
Finds and opens the file in the search path.
Definition files.cpp:162
@ FILE_READ
Definition filesys.h:111
@ FILE_WRITE
Definition filesys.h:111
void Sys_Error(const char *error,...)
Definition g_main.cpp:421
voidpf void uLong size
Definition ioapi.h:42
voidpf void * buf
Definition ioapi.h:42
void R_DrawChar(int x, int y, int num, uint32_t color)
Draws one 8*8 graphics character with 0 being transparent. It can be clipped to the top of the screen...
Definition r_draw.cpp:99
void R_DrawStretchImage(float x, float y, int w, int h, const image_t *image)
Definition r_draw.cpp:349
QGL_EXTERN GLuint GLchar GLuint * len
Definition r_gl.h:99
QGL_EXTERN GLfloat f
Definition r_gl.h:114
QGL_EXTERN GLint i
Definition r_gl.h:113
image_t * R_FindImage(const char *pname, imagetype_t type)
Finds or loads the given image.
Definition r_image.cpp:603
@ it_pic
Definition r_image.h:45
#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
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition shared.cpp:494
int displayLine
bool initialized
int currentLine
short text[CON_TEXTSIZE]
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition cvar.h:71
int UTF8_char_len(unsigned char c)
length of UTF-8 character starting with this byte.
Definition utf8.cpp:109