1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, v
18 */
19
20 //
21 // cl_console.c
22 //
23
24 #include "cl_local.h"
25
26 #define CON_TEXTSIZE 65536
27 #define CON_MAXTIMES 128
28
29 #define CON_NOTIFYTIMES ( clamp (con_notifylines->intVal, 1, CON_MAXTIMES) )
30
31 typedef struct console_s {
32 qBool initialized;
33
34 char text[CON_TEXTSIZE]; // console text
35 float times[CON_MAXTIMES]; // cls.realTime time the line was generated
36 // for transparent notify lines
37
38 int orMask;
39
40 qBool carriageReturn; // last newline was '\r'
41 int xOffset;
42
43 int lastColor; // color before last newline
44 int lastStyle; // style before last newline
45 int currentLine; // line where next message will be printed
46 int display; // bottom of console displays this line
47
48 float visLines;
49 int totalLines; // total lines in console scrollback
50 int lineWidth; // characters across screen
51
52 float currentDrop; // aproaches con_DropHeight at scr_conspeed->floatVal
53 float dropHeight; // 0.0 to 1.0 lines of console to display
54 } console_t;
55
56 static console_t cl_console;
57 static console_t cl_chatConsole;
58
59 /*
60 ================
61 CL_ClearNotifyLines
62 ================
63 */
CL_ClearNotifyLines(void)64 void CL_ClearNotifyLines (void)
65 {
66 memset (&cl_console.times, 0, sizeof (cl_console.times));
67 memset (&cl_chatConsole.times, 0, sizeof (cl_chatConsole.times));
68 }
69
70
71 /*
72 ================
73 CL_ConsoleClose
74 ================
75 */
CL_ConsoleClose(void)76 void CL_ConsoleClose (void)
77 {
78 cl_console.currentDrop = 0;
79 cl_chatConsole.currentDrop = 0;
80
81 Key_ClearTyping ();
82 CL_ClearNotifyLines ();
83 Key_ClearStates ();
84 }
85
86
87 /*
88 ================
89 CL_MoveConsoleDisplay
90 ================
91 */
CL_MoveConsoleDisplay(int value)92 void CL_MoveConsoleDisplay (int value)
93 {
94 cl_console.display += value;
95 if (cl_console.display > cl_console.currentLine)
96 cl_console.display = cl_console.currentLine;
97 }
98
99
100 /*
101 ================
102 CL_SetConsoleDisplay
103 ================
104 */
CL_SetConsoleDisplay(qBool top)105 void CL_SetConsoleDisplay (qBool top)
106 {
107 cl_console.display = cl_console.currentLine;
108 if (top)
109 cl_console.display -= cl_console.totalLines + 10;
110 }
111
112
113 /*
114 ================
115 CL_ConsoleCheckResize
116
117 If the line width has changed, reformat the buffer.
118 ================
119 */
CL_ResizeConsole(console_t * console)120 static void CL_ResizeConsole (console_t *console)
121 {
122 int width, oldWidth;
123 int numLines, numChars;
124 int oldTotalLines;
125 int i, j;
126 char tempbuf[CON_TEXTSIZE];
127 vec2_t charSize;
128
129 if (cls.refConfig.vidWidth < 1) {
130 // Video hasn't been initialized yet
131 console->lineWidth = (640 / 8) - 2;
132 console->totalLines = CON_TEXTSIZE / console->lineWidth;
133 memset (console->text, ' ', CON_TEXTSIZE);
134 }
135 else {
136 R_GetFontDimensions (NULL, 0, 0, 0, charSize);
137
138 width = (cls.refConfig.vidWidth / charSize[0]) - 2;
139 if (width == console->lineWidth)
140 return;
141
142 oldWidth = console->lineWidth;
143 console->lineWidth = width;
144
145 oldTotalLines = console->totalLines;
146 console->totalLines = CON_TEXTSIZE / console->lineWidth;
147
148 numLines = oldTotalLines;
149 if (console->totalLines < numLines)
150 numLines = console->totalLines;
151
152 numChars = oldWidth;
153 if (console->lineWidth < numChars)
154 numChars = console->lineWidth;
155
156 memcpy (tempbuf, console->text, CON_TEXTSIZE);
157 memset (console->text, ' ', CON_TEXTSIZE);
158
159 for (i=0 ; i<numLines ; i++) {
160 for (j=0 ; j<numChars ; j++) {
161 console->text[(console->totalLines - 1 - i) * console->lineWidth + j] =
162 tempbuf[((console->currentLine - i + oldTotalLines) % oldTotalLines) * oldWidth + j];
163 }
164 }
165
166 CL_ClearNotifyLines ();
167 }
168
169 console->currentLine = max (1, console->totalLines - 1);
170 console->display = console->currentLine;
171 }
172
CL_ConsoleCheckResize(void)173 void CL_ConsoleCheckResize (void)
174 {
175 CL_ResizeConsole (&cl_console);
176 CL_ResizeConsole (&cl_chatConsole);
177 }
178
179
180 /*
181 ===============
182 CL_ConsoleLinefeed
183 ===============
184 */
CL_ConsoleLinefeed(console_t * console,qBool skipNotify)185 static void CL_ConsoleLinefeed (console_t *console, qBool skipNotify)
186 {
187 // Line feed
188 if (console->display == console->currentLine)
189 console->display++;
190
191 console->currentLine++;
192 memset (&console->text[(console->currentLine%console->totalLines)*console->lineWidth], ' ', console->lineWidth);
193
194 // Set the time for notify lines
195 console->times[console->currentLine % CON_MAXTIMES] = (skipNotify) ? 0 : cls.realTime;
196 }
197
198
199 /*
200 ================
201 CL_ConsolePrintf
202
203 Handles cursor positioning, line wrapping, etc
204 All console printing must go through this in order to be logged to disk
205 If no console is visible, the text will appear at the top of the game window
206 ================
207 */
CL_PrintToConsole(console_t * console,comPrint_t flags,const char * txt)208 static void CL_PrintToConsole (console_t *console, comPrint_t flags, const char *txt)
209 {
210 int y, l;
211 int orMask;
212
213 if (!console->initialized)
214 return;
215
216 if (txt[0] == 1 || txt[0] == 2) {
217 orMask = 128;
218 txt++;
219 }
220 else
221 orMask = 0;
222
223 while (*txt) {
224 // Count word length
225 for (l=0 ; l<console->lineWidth ; l++) {
226 if (txt[l] <= ' ')
227 break;
228 }
229
230 // Word wrap
231 if (l != console->lineWidth && console->xOffset + l > console->lineWidth)
232 console->xOffset = 0;
233
234 if (console->carriageReturn) {
235 console->currentLine = max (1, console->currentLine - 1);
236 console->carriageReturn = qFalse;
237 }
238
239 if (!console->xOffset) {
240 // Feed a line
241 CL_ConsoleLinefeed (console, (flags & PRNT_CONSOLE));
242
243 y = console->currentLine % console->totalLines;
244 if (console->lastColor != -1 && y*console->lineWidth < CON_TEXTSIZE-2) {
245 console->text[y*console->lineWidth] = COLOR_ESCAPE;
246 console->text[y*console->lineWidth+1] = '0' + console->lastColor;
247 console->xOffset += 2;
248 }
249
250 if (console->lastStyle != -1 && y*console->lineWidth+console->xOffset < CON_TEXTSIZE-2) {
251 console->text[y*console->lineWidth+console->xOffset] = COLOR_ESCAPE;
252 console->text[y*console->lineWidth+console->xOffset+1] = console->lastStyle;
253 console->xOffset += 2;
254 }
255 }
256
257 switch (*txt) {
258 case '\n':
259 console->lastColor = -1;
260 console->lastStyle = -1;
261 console->xOffset = 0;
262 break;
263
264 case '\r':
265 console->lastColor = -1;
266 console->lastStyle = -1;
267 console->xOffset = 0;
268 console->carriageReturn = qTrue;
269 break;
270
271 default:
272 // Display character and advance
273 y = console->currentLine % console->totalLines;
274 console->text[y*console->lineWidth+console->xOffset] = *txt | orMask | console->orMask;
275 if (++console->xOffset >= console->lineWidth)
276 console->xOffset = 0;
277
278 // Get old color/style codes
279 if (Q_IsColorString (txt)) {
280 switch ((*(txt+1)) & 127) {
281 case 's':
282 case 'S':
283 if (console->lastStyle == 'S')
284 console->lastStyle = -1;
285 else
286 console->lastStyle = 'S';
287 break;
288
289 case 'r':
290 case 'R':
291 console->lastStyle = -1;
292 console->lastColor = -1;
293 break;
294
295 case COLOR_BLACK:
296 case COLOR_RED:
297 case COLOR_GREEN:
298 case COLOR_YELLOW:
299 case COLOR_BLUE:
300 case COLOR_CYAN:
301 case COLOR_MAGENTA:
302 case COLOR_WHITE:
303 case COLOR_GREY:
304 console->lastColor = Q_StrColorIndex (*(txt+1));
305 break;
306
307 default:
308 console->lastColor = -1;
309 break;
310 }
311 }
312 break;
313 }
314
315 txt++;
316 }
317 }
318
CL_ConsolePrintf(comPrint_t flags,const char * txt)319 void CL_ConsolePrintf (comPrint_t flags, const char *txt)
320 {
321 // Error/warning color coding
322 if (flags & PRNT_ERROR)
323 CL_PrintToConsole (&cl_console, flags, S_COLOR_RED);
324 else if (flags & PRNT_WARNING)
325 CL_PrintToConsole (&cl_console, flags, S_COLOR_YELLOW);
326
327 // High-bit character list
328 if (flags & PRNT_CHATHUD) {
329 cl_console.orMask = 128;
330 cl_chatConsole.orMask = 128;
331
332 // Regular console
333 if (con_chatHud->intVal == 2)
334 flags |= PRNT_CONSOLE;
335 }
336
337 // Regular console
338 CL_PrintToConsole (&cl_console, flags, txt);
339
340 // Chat console
341 if (flags & PRNT_CHATHUD) {
342 CL_PrintToConsole (&cl_chatConsole, flags, txt);
343
344 cl_console.orMask = 0;
345 cl_chatConsole.orMask = 0;
346 }
347 }
348
349 /*
350 ==============================================================================
351
352 CONSOLE COMMANDS
353
354 ==============================================================================
355 */
356
357 /*
358 ================
359 CL_ClearConsoleText_f
360 ================
361 */
CL_ClearConsoleText_f(void)362 static void CL_ClearConsoleText_f (void)
363 {
364 // Reset line locations and clear the buffers
365 cl_console.currentLine = max (1, cl_console.totalLines - 1);
366 cl_console.display = cl_console.currentLine;
367 memset (cl_console.text, ' ', CON_TEXTSIZE);
368
369 cl_chatConsole.currentLine = max (1, cl_chatConsole.totalLines - 1);
370 cl_chatConsole.display = cl_chatConsole.currentLine;
371 memset (cl_chatConsole.text, ' ', CON_TEXTSIZE);
372 }
373
374
375 /*
376 ================
377 CL_ConsoleDump_f
378
379 Save the console contents out to a file
380 ================
381 */
CL_ConsoleDump_f(void)382 static void CL_ConsoleDump_f (void)
383 {
384 int l, x;
385 char *line;
386 FILE *f;
387 char buffer[1024];
388 char name[MAX_OSPATH];
389
390 if (Cmd_Argc () != 2) {
391 Com_Printf (0, "usage: condump <filename>\n");
392 return;
393 }
394
395 if (strchr (Cmd_Argv (1), '.'))
396 Q_snprintfz (name, sizeof (name), "%s/condumps/%s", FS_Gamedir(), Cmd_Argv(1));
397 else
398 Q_snprintfz (name, sizeof (name), "%s/condumps/%s.txt", FS_Gamedir(), Cmd_Argv (1));
399
400 Com_Printf (0, "Dumped console text to %s.\n", name);
401 FS_CreatePath (name);
402 f = fopen (name, "w");
403 if (!f) {
404 Com_Printf (PRNT_ERROR, "ERROR: CL_ConsoleDump_f: couldn't open, make sure the name you attempted is valid (%s).\n", name);
405 return;
406 }
407
408 // Skip empty lines
409 for (l=cl_console.currentLine-cl_console.totalLines+1 ; l<=cl_console.currentLine ; l++) {
410 line = cl_console.text + (l%cl_console.totalLines)*cl_console.lineWidth;
411 for (x=0 ; x<cl_console.lineWidth ; x++)
412 if (line[x] != ' ')
413 break;
414 if (x != cl_console.lineWidth)
415 break;
416 }
417
418 // Write the remaining lines
419 buffer[cl_console.lineWidth] = 0;
420 for ( ; l<=cl_console.currentLine ; l++) {
421 line = cl_console.text + (l % cl_console.totalLines) * cl_console.lineWidth;
422 strncpy (buffer, line, cl_console.lineWidth);
423 for (x=cl_console.lineWidth-1 ; x>=0 ; x--) {
424 if (buffer[x] == ' ')
425 buffer[x] = '\0';
426 else
427 break;
428 }
429
430 // Handle high-bit text
431 for (x=0 ; buffer[x] ; x++)
432 buffer[x] &= 127;
433
434 fprintf (f, "%s\n", buffer);
435 }
436
437 fclose (f);
438 }
439
440
441 /*
442 ================
443 CL_ToggleConsole_f
444 ================
445 */
CL_ToggleConsole_f(void)446 void CL_ToggleConsole_f (void)
447 {
448 static keyDest_t oldKD;
449
450 // Kill the loading plaque
451 SCR_EndLoadingPlaque ();
452
453 Key_ClearTyping ();
454 CL_ClearNotifyLines ();
455 Key_ClearStates ();
456
457 if (Key_GetDest () == KD_CONSOLE) {
458 if (oldKD == KD_MESSAGE)
459 Key_SetDest (KD_GAME);
460 else
461 Key_SetDest (oldKD);
462 }
463 else {
464 oldKD = Key_GetDest ();
465 Key_SetDest (KD_CONSOLE);
466 }
467 }
468
469
470 /*
471 ================
472 CL_ToggleChat_f
473 ================
474 */
CL_ToggleChat_f(void)475 static void CL_ToggleChat_f (void)
476 {
477 Key_ClearTyping ();
478
479 if (Key_GetDest () == KD_CONSOLE) {
480 if (Com_ClientState () == CA_ACTIVE) {
481 CL_CGModule_ForceMenuOff ();
482 Key_SetDest (KD_GAME);
483 }
484 }
485 else
486 Key_SetDest (KD_CONSOLE);
487
488 CL_ClearNotifyLines ();
489 }
490
491
492 /*
493 ================
494 CL_MessageMode_f
495 ================
496 */
CL_MessageMode_f(void)497 static void CL_MessageMode_f (void)
498 {
499 if (Com_ClientState () != CA_ACTIVE)
500 return;
501
502 key_chatTeam = qFalse;
503 Key_SetDest (KD_MESSAGE);
504 }
505
506
507 /*
508 ================
509 CL_MessageMode2_f
510 ================
511 */
CL_MessageMode2_f(void)512 static void CL_MessageMode2_f (void)
513 {
514 if (Com_ClientState () != CA_ACTIVE)
515 return;
516
517 key_chatTeam = qTrue;
518 Key_SetDest (KD_MESSAGE);
519 }
520
521 /*
522 ==============================================================================
523
524 INIT / SHUTDOWN
525
526 ==============================================================================
527 */
528
529 /*
530 ================
531 CL_ConsoleInit
532 ================
533 */
CL_ConsoleInit(void)534 void CL_ConsoleInit (void)
535 {
536 if (dedicated->intVal)
537 return;
538
539 Cmd_AddCommand ("toggleconsole", CL_ToggleConsole_f, "Toggles displaying the console");
540 Cmd_AddCommand ("togglechat", CL_ToggleChat_f, "");
541 Cmd_AddCommand ("messagemode", CL_MessageMode_f, "");
542 Cmd_AddCommand ("messagemode2", CL_MessageMode2_f, "");
543 Cmd_AddCommand ("clear", CL_ClearConsoleText_f, "Clears the console buffer");
544 Cmd_AddCommand ("condump", CL_ConsoleDump_f, "Dumps the content of the console to file");
545
546 // Setup the console
547 memset (&cl_console, 0, sizeof (console_t));
548 cl_console.lineWidth = -1;
549 cl_console.totalLines = -1;
550 cl_console.lastColor = -1;
551 cl_console.lastStyle = -1;
552
553 // Setup the chat console
554 memset (&cl_chatConsole, 0, sizeof (console_t));
555 cl_chatConsole.lineWidth = -1;
556 cl_chatConsole.totalLines = -1;
557 cl_chatConsole.lastColor = -1;
558 cl_chatConsole.lastStyle = -1;
559
560 // Done
561 CL_ConsoleCheckResize ();
562
563 cl_console.initialized = qTrue;
564 cl_chatConsole.initialized = qTrue;
565 }
566
567 /*
568 ==============================================================================
569
570 DRAWING
571
572 ==============================================================================
573 */
574
575 /*
576 ================
577 CL_DrawInput
578
579 The input line scrolls horizontally if typing goes beyond the right edge
580 ================
581 */
CL_DrawInput(void)582 static void CL_DrawInput (void)
583 {
584 char *text;
585 int lastColor, lastStyle;
586 int colorCursorPos;
587 int byteOfs;
588 int byteLen;
589 vec2_t charSize;
590
591 if (Key_GetDest () == KD_MENU)
592 return;
593
594 // Don't draw anything (always draw if not active)
595 if (Key_GetDest () != KD_CONSOLE && Com_ClientState () == CA_ACTIVE)
596 return;
597
598 R_GetFontDimensions (NULL, 0, 0, 0, charSize);
599
600 text = key_consoleBuffer[key_consoleEditLine];
601
602 // Convert byte offset to visible character count
603 colorCursorPos = Q_ColorCharCount (text, key_consoleCursorPos);
604
605 // Prestep if horizontally scrolling
606 if (colorCursorPos >= cl_console.lineWidth + 1) {
607 byteOfs = Q_ColorCharOffset (text, colorCursorPos - cl_console.lineWidth);
608 lastColor = Q_ColorStrLastColor (text, byteOfs);
609 lastStyle = Q_ColorStrLastStyle (text, byteOfs);
610 text += byteOfs;
611 colorCursorPos = cl_console.lineWidth;
612 }
613 else {
614 lastColor = Q_StrColorIndex (COLOR_WHITE);
615 lastStyle = 0;
616 }
617
618 byteLen = Q_ColorCharOffset (text, cl_console.lineWidth);
619
620 // Draw it
621 R_DrawStringLen (NULL, 8, cl_console.visLines - (charSize[1] * 2), 0, 0, lastStyle, text, byteLen, Q_strColorTable[lastColor]);
622
623 // Add the cursor
624 if ((Sys_UMilliseconds()>>8)&1)
625 R_DrawChar (NULL, 8 + ((colorCursorPos - (key_insertOn ? 0.3 : 0)) * charSize[0]), cl_console.visLines - (charSize[1] * 2),
626 0, 0, 0, key_insertOn ? '|' : 11, Q_colorWhite);
627 }
628
629
630 /*
631 ================
632 CL_DrawNotify
633
634 Draws the last few lines of output transparently over the game top
635 ================
636 */
CL_DrawNotify(void)637 static void CL_DrawNotify (void)
638 {
639 int v, i, time;
640 int notifyLines;
641 int totalLines;
642 char *str;
643 float newScale;
644 vec4_t color;
645 vec2_t charSize;
646
647 Vec4Copy (Q_colorWhite, color);
648
649 // Make larger if desired
650 R_GetFontDimensions (NULL, 0, 0, 0, charSize);
651 if (con_notifylarge->intVal) {
652 newScale = r_fontScale->floatVal * 1.25f;
653
654 charSize[0] *= 1.25f;
655 charSize[1] *= 1.25f;
656 }
657 else {
658 newScale = r_fontScale->floatVal;
659 }
660
661 // Render notify lines
662 if (con_notifylines->intVal) {
663 for (totalLines=0, i=cl_console.currentLine ; i>cl_console.currentLine-CON_MAXTIMES+1 ; i--) {
664 if (totalLines >= CON_NOTIFYTIMES)
665 break;
666
667 time = cl_console.times[i % CON_MAXTIMES];
668 if (time == 0)
669 continue;
670 time = cls.realTime - time;
671 if (time > con_notifytime->floatVal * 1000)
672 continue;
673 totalLines++;
674 }
675
676 for (v=charSize[1]*(totalLines-1), notifyLines=0, i=cl_console.currentLine ; i>cl_console.currentLine-CON_MAXTIMES+1 ; i--) {
677 if (notifyLines >= CON_NOTIFYTIMES)
678 break;
679
680 time = cl_console.times[i % CON_MAXTIMES];
681 if (time == 0)
682 continue;
683 time = cls.realTime - time;
684 if (time > con_notifytime->floatVal * 1000)
685 continue;
686 notifyLines++;
687
688 if (con_notifyfade->intVal)
689 color[3] = 0.25 + 0.75 * (con_notifytime->floatVal - (time * 0.001)) / con_notifytime->floatVal;
690
691 R_DrawStringLen (NULL, 8, v, newScale, newScale, 0, cl_console.text + (i % cl_console.totalLines)*cl_console.lineWidth, cl_console.lineWidth, color);
692
693 v -= charSize[1];
694 }
695 v = charSize[1] * totalLines;
696 }
697 else {
698 v = 0;
699 }
700
701 //
702 // Print messagemode input
703 //
704 if (Key_GetDest () == KD_MESSAGE) {
705 int skip;
706 int lastColor, lastStyle;
707 int colorCursorPos;
708 int byteOfs;
709
710 if (key_chatTeam)
711 skip = R_DrawString (NULL, 4, v, newScale, newScale, 0, "say_team:", Q_colorWhite) + 1;
712 else
713 skip = R_DrawString (NULL, 4, v, newScale, newScale, 0, "say:", Q_colorWhite) + 1;
714
715 str = key_chatBuffer[key_chatEditLine];
716
717 // Convert byte offset to visible character count
718 colorCursorPos = Q_ColorCharCount (str, key_chatCursorPos) + skip + 1;
719
720 // Prestep if horizontally scrolling
721 if (colorCursorPos >= (int)(cls.refConfig.vidWidth/charSize[0])) {
722 byteOfs = Q_ColorCharOffset (str, colorCursorPos - (int)(cls.refConfig.vidWidth/charSize[0]));
723
724 lastColor = Q_ColorStrLastColor (str, byteOfs);
725 lastStyle = Q_ColorStrLastStyle (str, byteOfs);
726
727 str += byteOfs;
728 }
729 else {
730 lastColor = Q_StrColorIndex (COLOR_WHITE);
731 lastStyle = 0;
732 }
733
734 R_DrawString (NULL, skip * charSize[0], v, newScale, newScale, lastStyle, str, Q_strColorTable[lastColor]);
735
736 // Add cursor
737 if ((Sys_UMilliseconds()>>8)&1) {
738 int charCount = Q_ColorCharCount (key_chatBuffer[key_chatEditLine], key_chatCursorPos) + skip;
739 if (charCount > (int)(cls.refConfig.vidWidth/charSize[0]) - 1)
740 charCount = (int)(cls.refConfig.vidWidth/charSize[0]) - 1;
741
742 R_DrawChar (NULL, ((charCount - (key_insertOn ? 0.3 : 0)) * charSize[0]), v, newScale, newScale, 0,
743 key_insertOn ? '|' : 11, Q_colorWhite);
744 }
745
746 v += charSize[1];
747 }
748 }
749
750
751 /*
752 ==================
753 CL_DrawChatHud
754 ==================
755 */
CL_DrawChatHud(void)756 static void CL_DrawChatHud (void)
757 {
758 int totalLines, v, i;
759 char *text;
760 vec2_t charSize;
761
762 if (Com_ClientState () != CA_ACTIVE)
763 return;
764 if (Key_GetDest () == KD_MENU)
765 return;
766 if (cls.mapLoading)
767 return;
768
769 R_GetFontDimensions (NULL, 0, 0, 0, charSize);
770
771 if (con_chatHudPosY->floatVal < 0)
772 v = cls.refConfig.vidHeight - (charSize[1] * -con_chatHudPosY->floatVal);
773 else
774 v = cls.refConfig.vidHeight + (charSize[1] * con_chatHudPosY->floatVal);
775
776 totalLines = 0;
777 for (i=cl_chatConsole.currentLine ; i>cl_chatConsole.currentLine-CON_MAXTIMES+1 ; i--) {
778 if (totalLines == con_chatHudLines->intVal)
779 break;
780
781 text = cl_chatConsole.text + (i % cl_chatConsole.totalLines)*cl_chatConsole.lineWidth;
782 R_DrawStringLen (NULL, con_chatHudPosX->floatVal, v, 0, 0, con_chatHudShadow->intVal ? FS_SHADOW : 0, text, cl_chatConsole.lineWidth, Q_colorWhite);
783
784 totalLines++;
785 v -= charSize[1];
786 }
787 }
788
789
790 /*
791 ==================
792 CL_RunConsole
793
794 Scroll it up or down
795 ==================
796 */
CL_RunConsole(void)797 static void CL_RunConsole (void)
798 {
799 CL_ConsoleCheckResize ();
800
801 // Decide on the height of the console
802 if (Key_GetDest () == KD_CONSOLE)
803 cl_console.dropHeight = con_drop->floatVal;
804 else
805 cl_console.dropHeight = 0; // none visible
806
807 if (cl_console.dropHeight < cl_console.currentDrop) {
808 cl_console.currentDrop -= scr_conspeed->floatVal*cls.refreshFrameTime;
809 if (cl_console.dropHeight > cl_console.currentDrop)
810 cl_console.currentDrop = cl_console.dropHeight;
811 }
812 else if (cl_console.dropHeight > cl_console.currentDrop) {
813 cl_console.currentDrop += scr_conspeed->floatVal*cls.refreshFrameTime;
814 if (cl_console.dropHeight < cl_console.currentDrop)
815 cl_console.currentDrop = cl_console.dropHeight;
816 }
817 }
818
819
820 /*
821 ================
822 CL_DrawConsole
823 ================
824 */
CL_DrawConsole(void)825 void CL_DrawConsole (void)
826 {
827 float frac = 0.0f;
828 int i, row;
829 float x, y, lines;
830 vec4_t conColor;
831 char version[32];
832 vec2_t charSize;
833
834 R_GetFontDimensions (NULL, 0, 0, 0, charSize);
835
836 // Advance for next frame
837 CL_RunConsole ();
838
839 // Draw the chat hud
840 if (con_chatHud->intVal && con_chatHudLines->intVal > 0)
841 CL_DrawChatHud ();
842
843 if (cl_console.currentDrop)
844 frac = cl_console.currentDrop;
845 else if (Com_ClientState () == CA_ACTIVE && (Key_GetDest () == KD_GAME || Key_GetDest () == KD_MESSAGE) && !cls.mapLoading && !cl.cin.time) {
846 CL_DrawNotify (); // Only draw notify in game
847 return;
848 }
849
850 // Check if it's even visible
851 cl_console.visLines = cls.refConfig.vidHeight * clamp (frac, 0, 1);
852 if (cl_console.visLines <= 0)
853 return;
854
855 // Draw the background
856 conColor[0] = Q_colorWhite[0];
857 conColor[1] = Q_colorWhite[1];
858 conColor[2] = Q_colorWhite[2];
859 conColor[3] = con_alpha->floatVal;
860 R_DrawPic (clMedia.consoleShader, 0, 0, -(cls.refConfig.vidHeight - cls.refConfig.vidHeight*frac), cls.refConfig.vidWidth, cls.refConfig.vidHeight, 0, 0, 1, 1, conColor);
861
862 // Version
863 Q_snprintfz (version, sizeof (version), "EGL v%s", EGL_VERSTR);
864 R_DrawString (NULL, cls.refConfig.vidWidth - (strlen(version) * charSize[0]) - 2,
865 cl_console.visLines - charSize[1],
866 0, 0, FS_SHADOW, version, Q_colorCyan);
867
868 // Time if desired
869 if (con_clock->intVal) {
870 char clockStr[16];
871 time_t ctime;
872 struct tm *ltime;
873
874 ctime = time (NULL);
875 ltime = localtime (&ctime);
876 strftime (clockStr, sizeof (clockStr), "%I:%M %p", ltime);
877
878 // Kill the initial zero
879 if (clockStr[0] == '0') {
880 for (i=0 ; i<(int)(strlen (clockStr) + 1) ; i++)
881 clockStr[i] = clockStr[i+1];
882 clockStr[i+1] = '\0';
883 }
884
885 R_DrawString (NULL, cls.refConfig.vidWidth - (strlen (clockStr) * charSize[0]) - 2,
886 cl_console.visLines - (charSize[1] * 2),
887 0, 0, FS_SHADOW, clockStr, Q_colorCyan);
888 }
889
890 // Draw the console text
891 y = cl_console.visLines - (charSize[1] * 3); // start point (from the bottom)
892 lines = (y / charSize[1]); // lines of text to draw
893
894 // Draw arrows to show the buffer is backscrolled
895 if (cl_console.display != cl_console.currentLine) {
896 for (x=0 ; x<(cl_console.lineWidth+1)*charSize[0] ; x+=charSize[0]*2)
897 R_DrawChar (NULL, x + (charSize[0] * 0.5), y, 0, 0, FS_SHADOW, '^', Q_colorRed);
898
899 y -= charSize[1];
900 lines--;
901 }
902
903 // Draw the print, from the bottom up
904 for (i=0, row=cl_console.display ; i<lines ; i++, y-=charSize[1], row--) {
905 if (row < 0)
906 break;
907 if (cl_console.currentLine - row >= cl_console.totalLines)
908 break; // past scrollback wrap point
909 if (y < -charSize[1])
910 break;
911
912 R_DrawStringLen (NULL, 8, y, 0, 0, 0, cl_console.text + (row % cl_console.totalLines) * cl_console.lineWidth, cl_console.lineWidth, Q_colorWhite);
913 }
914
915 // Draw the input prompt, user text, and cursor if desired
916 CL_DrawInput ();
917 }
918