UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
cl_console.cpp
Go to the documentation of this file.
1 
6 /*
7 All original material Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 Original file from Quake 2 v3.21: quake2-2.31/client/console.c
10 Copyright (C) 1997-2001 Id Software, Inc.
11 
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 as published by the Free Software Foundation; either version 2
15 of the License, or (at your option) any later version.
16 
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 
21 See the GNU General Public License for more details.
22 
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, 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 */
39 static 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 
57 typedef struct {
59 
60  short text[CON_TEXTSIZE];
62  int pos;
65  int lineWidth;
66  int totalLines;
68  int visLines;
69 } console_t;
70 
71 static console_t con;
75 const int con_fontHeight = 12;
76 const int con_fontWidth = 10;
77 const int con_fontShift = 3;
78 
79 static 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 
92 static 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 
105 void 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 
132 static void Key_ClearTyping (void)
133 {
134  keyLines[editLine][1] = 0; /* clear any typing */
135  keyLinePos = 1;
136 }
137 
139 {
140  Key_ClearTyping();
141 
142  if (cls.keyDest == key_console) {
144  } else {
146  }
147 }
148 
149 static void Con_ToggleChat_f (void)
150 {
151  Key_ClearTyping();
152 
153  if (cls.keyDest == key_console) {
154  if (cls.state == ca_active)
156  } else
158 }
159 
163 static void Con_Clear_f (void)
164 {
165  Con_Clear();
166 }
167 
172 void 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 
184 void Con_CheckResize (void)
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 
230  ScopedFile f;
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 
256  ScopedFile f;
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 
276 void 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);
294  con.totalLines = lengthof(con.text) / con.lineWidth;
295  con.initialized = true;
296 
297  Com_Printf("Console initialized.\n");
298 }
299 
300 
301 static 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 
318 void 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 ==============================================================================
386 DRAWING
387 ==============================================================================
388 */
389 
393 void Con_Close (void)
394 {
395  if (cls.keyDest == key_console)
397 }
398 
402 static 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 
443 void 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 */
498  Con_DrawInput();
499 }
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 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 Con_DrawString(const char *txt, int x, int y, unsigned int width)
Definition: cl_console.cpp:105
void Sys_Error(const char *error,...)
Definition: g_main.cpp:421
static cvar_t * con_notifytime
Definition: cl_console.cpp:72
int FS_OpenFile(const char *filename, qFILE *file, filemode_t mode)
Finds and opens the file in the search path.
Definition: files.cpp:162
#define ColorIndex(c)
Definition: cl_console.cpp:36
#define Q_IsColorString(p)
Definition: common.h:215
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition: cvar.h:71
int currentLine
Definition: cl_console.cpp:61
static void Con_Clear(void)
Definition: cl_console.cpp:79
static console_t con
Definition: cl_console.cpp:71
bool file() const
Definition: filesys.h:206
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
int UTF8_char_len(unsigned char c)
length of UTF-8 character starting with this byte.
Definition: utf8.cpp:109
void Con_Init(void)
Definition: cl_console.cpp:276
Shared game type headers.
static cvar_t * con_history
Definition: cl_console.cpp:73
viddef_t viddef
Definition: cl_video.cpp:34
void R_DrawStretchImage(float x, float y, int w, int h, const image_t *image)
Definition: r_draw.cpp:349
bool initialized
Definition: cl_console.cpp:58
unsigned width
Definition: cl_video.h:44
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
void Con_Close(void)
Hide the gameconsole if active.
Definition: cl_console.cpp:393
int virtualHeight
Definition: cl_video.h:74
FILE * getFile() const
Definition: filesys.h:214
int totalLines
Definition: cl_console.cpp:66
Definition: r_image.h:45
int displayLine
Definition: cl_console.cpp:63
#define CON_COLOR_WHITE
Definition: common.h:233
int integer
Definition: cvar.h:81
voidpf void * buf
Definition: ioapi.h:42
#define CVAR_ARCHIVE
Definition: cvar.h:40
const int con_fontWidth
Definition: cl_console.cpp:76
image_t * R_FindImage(const char *pname, imagetype_t type)
Finds or loads the given image.
Definition: r_image.cpp:603
Header file for keyboard handler.
static void Key_ClearTyping(void)
Definition: cl_console.cpp:132
void Key_SetDest(keydest_t keyDest)
Sets the keyDest in cls.
Definition: cl_keys.cpp:815
client_static_t cls
Definition: cl_main.cpp:83
static cvar_t * con_background
Definition: cl_console.cpp:74
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
short text[CON_TEXTSIZE]
Definition: cl_console.cpp:60
int visLines
Definition: cl_console.cpp:68
#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
GLsizei size
Definition: r_gl.h:152
#define OBJZERO(obj)
Definition: shared.h:178
int lineWidth
Definition: cl_console.cpp:65
void Con_Print(const char *txt)
Handles cursor positioning, line wrapping, etc All console printing must go through this in order to ...
Definition: cl_console.cpp:318
void Con_Scroll(int scroll)
Scrolls the console.
Definition: cl_console.cpp:172
void Con_SaveConsoleHistory(void)
Stores the console history.
Definition: cl_console.cpp:250
int editLine
Definition: cl_keys.cpp:42
char keyLines[MAXKEYLINES][MAXCMDLINE]
Definition: cl_keys.cpp:37
void Con_LoadConsoleHistory(void)
Load the console history.
Definition: cl_console.cpp:223
static void Con_DrawText(const short *text, int x, int y, size_t width)
Definition: cl_console.cpp:92
viddefContext_t context
Definition: cl_video.h:67
#define MAXKEYLINES
Definition: cl_keys.h:186
#define VID_NORM_WIDTH
Definition: cl_renderer.h:40
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
#define CON_COLOR_GREEN
Definition: common.h:228
static void Con_DrawInput(void)
The input line scrolls horizontally if typing goes beyond the right edge.
Definition: cl_console.cpp:402
static void Con_ToggleChat_f(void)
Definition: cl_console.cpp:149
#define MAXCMDLINE
Definition: common.h:307
#define CONSOLE_CURSOR_CHAR
Definition: cl_console.cpp:54
void Con_DrawConsole(float frac)
Draws the console with the solid background.
Definition: cl_console.cpp:443
QGL_EXTERN GLint i
Definition: r_gl.h:113
QGL_EXTERN GLuint GLchar GLuint * len
Definition: r_gl.h:99
uint32_t keyLinePos
Definition: cl_keys.cpp:38
const int con_fontShift
Definition: cl_console.cpp:77
static void Con_Clear_f(void)
Clears the console buffer.
Definition: cl_console.cpp:163
#define N_(String)
Definition: cl_shared.h:45
int virtualWidth
Definition: cl_video.h:74
#define lengthof(x)
Definition: shared.h:105
connstate_t state
Definition: client.h:55
Primary header for client.
static const uint32_t g_color_table[]
Definition: cl_console.cpp:39
unsigned height
Definition: cl_video.h:45
void Con_ToggleConsole_f(void)
Definition: cl_console.cpp:138
#define CONSOLE_CHAR_ALIGN
Definition: cl_console.cpp:51
int historyLine
Definition: cl_keys.cpp:43
Console header file.
void Con_CheckResize(void)
If the line width has changed, reformat the buffer.
Definition: cl_console.cpp:184
const int con_fontHeight
Definition: cl_console.cpp:75
keydest_t keyDest
Definition: client.h:56
int CL_Milliseconds(void)
Definition: cl_main.cpp:1208
static void Con_Linefeed(void)
Definition: cl_console.cpp:301
int FS_Write(const void *buffer, int len, qFILE *f)
Properly handles partial writes.
Definition: files.cpp:1511
#define CONSOLE_HISTORY_FILENAME
Definition: cl_console.cpp:55
#define CON_TEXTSIZE
Definition: cl_console.cpp:53