1 /*
2 Copyright (C) 1996-1997 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, Boston, MA 02111-1307, USA.
18
19 */
20 // console.c
21
22 #include "quakedef.h"
23
24 int con_ormask;
25 console_t con_main;
26 console_t con_chat;
27 console_t *con; // point to either con_main or con_chat
28
29 int con_linewidth; // characters across screen
30 int con_totallines; // total lines in console scrollback
31
32 float con_cursorspeed = 4;
33
34
35 cvar_t con_notifytime = CVAR2("con_notifytime","3"); //seconds
36
37 #define NUM_CON_TIMES 4
38 float con_times[NUM_CON_TIMES]; // realtime time the line was generated
39 // for transparent notify lines
40
41 int con_vislines;
42 int con_notifylines; // scan lines to clear for notify lines
43
44 qboolean con_debuglog;
45
46 #define MAXCMDLINE 256
47 extern char key_lines[32][MAXCMDLINE];
48 extern int edit_line;
49 extern int key_linepos;
50
51
52 qboolean con_initialized;
53
54
Key_ClearTyping(void)55 void Key_ClearTyping (void)
56 {
57 key_lines[edit_line][1] = 0; // clear any typing
58 key_linepos = 1;
59 }
60
61 /*
62 ================
63 Con_ToggleConsole_f
64 ================
65 */
Con_ToggleConsole_f(void)66 void Con_ToggleConsole_f (void)
67 {
68 Key_ClearTyping ();
69
70 if (key_dest == key_console)
71 {
72 if (cls.state == ca_active)
73 key_dest = key_game;
74 }
75 else
76 key_dest = key_console;
77
78 Con_ClearNotify ();
79 }
80
81 /*
82 ================
83 Con_ToggleChat_f
84 ================
85 */
Con_ToggleChat_f(void)86 void Con_ToggleChat_f (void)
87 {
88 Key_ClearTyping ();
89
90 if (key_dest == key_console)
91 {
92 if (cls.state == ca_active)
93 key_dest = key_game;
94 }
95 else
96 key_dest = key_console;
97
98 Con_ClearNotify ();
99 }
100
101 /*
102 ================
103 Con_Clear_f
104 ================
105 */
Con_Clear_f(void)106 void Con_Clear_f (void)
107 {
108 Q_memset (con_main.text, ' ', CON_TEXTSIZE);
109 Q_memset (con_chat.text, ' ', CON_TEXTSIZE);
110 }
111
112
113 /*
114 ================
115 Con_ClearNotify
116 ================
117 */
Con_ClearNotify(void)118 void Con_ClearNotify (void)
119 {
120 int i;
121
122 for (i=0 ; i<NUM_CON_TIMES ; i++)
123 con_times[i] = 0;
124 }
125
126
127 /*
128 ================
129 Con_MessageMode_f
130 ================
131 */
Con_MessageMode_f(void)132 void Con_MessageMode_f (void)
133 {
134 chat_team = false;
135 key_dest = key_message;
136 }
137
138 /*
139 ================
140 Con_MessageMode2_f
141 ================
142 */
Con_MessageMode2_f(void)143 void Con_MessageMode2_f (void)
144 {
145 chat_team = true;
146 key_dest = key_message;
147 }
148
149 /*
150 ================
151 Con_Resize
152
153 ================
154 */
Con_Resize(console_t * con)155 void Con_Resize (console_t *con)
156 {
157 int i, j, width, oldwidth, oldtotallines, numlines, numchars;
158 char tbuf[CON_TEXTSIZE];
159
160 width = (vid.width >> 3) - 2;
161
162 if (width == con_linewidth)
163 return;
164
165 if (width < 1) // video hasn't been initialized yet
166 {
167 width = 38;
168 con_linewidth = width;
169 con_totallines = CON_TEXTSIZE / con_linewidth;
170 Q_memset (con->text, ' ', CON_TEXTSIZE);
171 }
172 else
173 {
174 oldwidth = con_linewidth;
175 con_linewidth = width;
176 oldtotallines = con_totallines;
177 con_totallines = CON_TEXTSIZE / con_linewidth;
178 numlines = oldtotallines;
179
180 if (con_totallines < numlines)
181 numlines = con_totallines;
182
183 numchars = oldwidth;
184
185 if (con_linewidth < numchars)
186 numchars = con_linewidth;
187
188 Q_memcpy (tbuf, con->text, CON_TEXTSIZE);
189 Q_memset (con->text, ' ', CON_TEXTSIZE);
190
191 for (i=0 ; i<numlines ; i++)
192 {
193 for (j=0 ; j<numchars ; j++)
194 {
195 con->text[(con_totallines - 1 - i) * con_linewidth + j] =
196 tbuf[((con->current - i + oldtotallines) %
197 oldtotallines) * oldwidth + j];
198 }
199 }
200
201 Con_ClearNotify ();
202 }
203
204 con->current = con_totallines - 1;
205 con->display = con->current;
206 }
207
208
209 /*
210 ================
211 Con_CheckResize
212
213 If the line width has changed, reformat the buffer.
214 ================
215 */
Con_CheckResize(void)216 void Con_CheckResize (void)
217 {
218 Con_Resize (&con_main);
219 Con_Resize (&con_chat);
220 }
221
222
223 /*
224 ================
225 Con_Init
226 ================
227 */
Con_Init(void)228 void Con_Init (void)
229 {
230 con_debuglog = COM_CheckParm("-condebug");
231
232 con = &con_main;
233 con_linewidth = -1;
234 Con_CheckResize ();
235
236 Con_Printf ("Console initialized.\n");
237
238 //
239 // register our commands
240 //
241 Cvar_RegisterVariable (&con_notifytime);
242
243 Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
244 Cmd_AddCommand ("togglechat", Con_ToggleChat_f);
245 Cmd_AddCommand ("messagemode", Con_MessageMode_f);
246 Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
247 Cmd_AddCommand ("clear", Con_Clear_f);
248 con_initialized = true;
249 }
250
251
252 /*
253 ===============
254 Con_Linefeed
255 ===============
256 */
Con_Linefeed(void)257 void Con_Linefeed (void)
258 {
259 con->x = 0;
260 if (con->display == con->current)
261 con->display++;
262 con->current++;
263 Q_memset (&con->text[(con->current%con_totallines)*con_linewidth]
264 , ' ', con_linewidth);
265 }
266
267 /*
268 ================
269 Con_Print
270
271 Handles cursor positioning, line wrapping, etc
272 All console printing must go through this in order to be logged to disk
273 If no console is visible, the notify window will pop up.
274 ================
275 */
Con_Print(char * txt)276 void Con_Print (char *txt)
277 {
278 int y;
279 int c, l;
280 static int cr;
281 int mask;
282
283 if (txt[0] == 1 || txt[0] == 2)
284 {
285 mask = 128; // go to colored text
286 txt++;
287 }
288 else
289 mask = 0;
290
291
292 while ( (c = *txt) )
293 {
294 // count word length
295 for (l=0 ; l< con_linewidth ; l++)
296 if ( txt[l] <= ' ')
297 break;
298
299 // word wrap
300 if (l != con_linewidth && (con->x + l > con_linewidth) )
301 con->x = 0;
302
303 txt++;
304
305 if (cr)
306 {
307 con->current--;
308 cr = false;
309 }
310
311
312 if (!con->x)
313 {
314 Con_Linefeed ();
315 // mark time for transparent overlay
316 if (con->current >= 0)
317 con_times[con->current % NUM_CON_TIMES] = realtime;
318 }
319
320 switch (c)
321 {
322 case '\n':
323 con->x = 0;
324 break;
325
326 case '\r':
327 con->x = 0;
328 cr = 1;
329 break;
330
331 default: // display character and advance
332 y = con->current % con_totallines;
333 con->text[y*con_linewidth+con->x] = c | mask | con_ormask;
334 con->x++;
335 if (con->x >= con_linewidth)
336 con->x = 0;
337 break;
338 }
339
340 }
341 }
342
343
344 /*
345 ================
346 Con_Printf
347
348 Handles cursor positioning, line wrapping, etc
349 ================
350 */
351 #define MAXPRINTMSG 4096
352 // FIXME: make a buffer size safe vsprintf?
Con_Printf(char * fmt,...)353 void Con_Printf (char *fmt, ...)
354 {
355 va_list argptr;
356 char msg[MAXPRINTMSG];
357 static qboolean inupdate;
358
359 va_start (argptr,fmt);
360 vsprintf (msg,fmt,argptr);
361 va_end (argptr);
362
363 // also echo to debugging console
364 Sys_Printf ("%s", msg); // also echo to debugging console
365
366 // log all messages to file
367 if (con_debuglog)
368 Sys_DebugLog(va("%s/qconsole.log",com_gamedir), "%s", msg);
369
370 if (!con_initialized)
371 return;
372
373 // write it to the scrollable buffer
374 Con_Print (msg);
375
376 // update the screen immediately if the console is displayed
377 if (cls.state != ca_active)
378 {
379 // protect against infinite loop if something in SCR_UpdateScreen calls
380 // Con_Printd
381 if (!inupdate)
382 {
383 inupdate = true;
384 SCR_UpdateScreen ();
385 inupdate = false;
386 }
387 }
388 }
389
390 /*
391 ================
392 Con_DPrintf
393
394 A Con_Printf that only shows up if the "developer" cvar is set
395 ================
396 */
Con_DPrintf(char * fmt,...)397 void Con_DPrintf (char *fmt, ...)
398 {
399 va_list argptr;
400 char msg[MAXPRINTMSG];
401
402 if (!developer.value)
403 return; // don't confuse non-developers with techie stuff...
404
405 va_start (argptr,fmt);
406 vsprintf (msg,fmt,argptr);
407 va_end (argptr);
408
409 Con_Printf ("%s", msg);
410 }
411
412 /*
413 ==============================================================================
414
415 DRAWING
416
417 ==============================================================================
418 */
419
420
421 /*
422 ================
423 Con_DrawInput
424
425 The input line scrolls horizontally if typing goes beyond the right edge
426 ================
427 */
Con_DrawInput(void)428 void Con_DrawInput (void)
429 {
430 int y;
431 int i;
432 char *text;
433
434 if (key_dest != key_console && cls.state == ca_active)
435 return; // don't draw anything (allways draw if not active)
436
437 text = key_lines[edit_line];
438
439 // add the cursor frame
440 text[key_linepos] = 10+((int)(realtime*con_cursorspeed)&1);
441
442 // fill out remainder with spaces
443 for (i=key_linepos+1 ; i< con_linewidth ; i++)
444 text[i] = ' ';
445
446 // prestep if horizontally scrolling
447 if (key_linepos >= con_linewidth)
448 text += 1 + key_linepos - con_linewidth;
449
450 // draw it
451 y = con_vislines-22;
452
453 for (i=0 ; i<con_linewidth ; i++)
454 Draw_Character ( (i+1)<<3, con_vislines - 22, text[i]);
455
456 // remove cursor
457 key_lines[edit_line][key_linepos] = 0;
458 }
459
460
461 /*
462 ================
463 Con_DrawNotify
464
465 Draws the last few lines of output transparently over the game top
466 ================
467 */
Con_DrawNotify(void)468 void Con_DrawNotify (void)
469 {
470 int x, v;
471 char *text;
472 int i;
473 float time;
474 char *s;
475 int skip;
476
477 v = 0;
478 for (i= con->current-NUM_CON_TIMES+1 ; i<=con->current ; i++)
479 {
480 if (i < 0)
481 continue;
482 time = con_times[i % NUM_CON_TIMES];
483 if (time == 0)
484 continue;
485 time = realtime - time;
486 if (time > con_notifytime.value)
487 continue;
488 text = con->text + (i % con_totallines)*con_linewidth;
489
490 clearnotify = 0;
491 scr_copytop = 1;
492
493 for (x = 0 ; x < con_linewidth ; x++)
494 Draw_Character ( (x+1)<<3, v, text[x]);
495
496 v += 8;
497 }
498
499
500 if (key_dest == key_message)
501 {
502 clearnotify = 0;
503 scr_copytop = 1;
504
505 if (chat_team)
506 {
507 Draw_String (8, v, "say_team:");
508 skip = 11;
509 }
510 else
511 {
512 Draw_String (8, v, "say:");
513 skip = 5;
514 }
515
516 s = chat_buffer;
517 if (chat_bufferlen > (int) ((vid.width>>3)-(skip+1)))
518 s += chat_bufferlen - ((vid.width>>3)-(skip+1));
519 x = 0;
520 while(s[x])
521 {
522 Draw_Character ( (x+skip)<<3, v, s[x]);
523 x++;
524 }
525 Draw_Character ( (x+skip)<<3, v, 10+((int)(realtime*con_cursorspeed)&1));
526 v += 8;
527 }
528
529 if (v > con_notifylines)
530 con_notifylines = v;
531 }
532
533 /*
534 ================
535 Con_DrawConsole
536
537 Draws the console with the solid background
538 ================
539 */
Con_DrawConsole(int lines)540 void Con_DrawConsole (int lines)
541 {
542 int i, j, x, y, n;
543 int rows;
544 char *text;
545 int row;
546 char dlbar[1024];
547
548 if (lines <= 0)
549 return;
550
551 // draw the background
552 Draw_ConsoleBackground (lines);
553
554 // draw the text
555 con_vislines = lines;
556
557 // changed to line things up better
558 rows = (lines-22)>>3; // rows of text to draw
559
560 y = lines - 30;
561
562 // draw from the bottom up
563 if (con->display != con->current)
564 {
565 // draw arrows to show the buffer is backscrolled
566 for (x=0 ; x<con_linewidth ; x+=4)
567 Draw_Character ( (x+1)<<3, y, '^');
568
569 y -= 8;
570 rows--;
571 }
572
573 row = con->display;
574 for (i=0 ; i<rows ; i++, y-=8, row--)
575 {
576 if (row < 0)
577 break;
578 if (con->current - row >= con_totallines)
579 break; // past scrollback wrap point
580
581 text = con->text + (row % con_totallines)*con_linewidth;
582
583 for (x=0 ; x<con_linewidth ; x++)
584 Draw_Character ( (x+1)<<3, y, text[x]);
585 }
586
587 // draw the download bar
588 // figure out width
589 if (cls.download) {
590 if ((text = strrchr(cls.downloadname, '/')) != NULL)
591 text++;
592 else
593 text = cls.downloadname;
594
595 x = con_linewidth - ((con_linewidth * 7) / 40);
596 y = x - strlen(text) - 8;
597 i = con_linewidth/3;
598 if ((int) strlen(text) > i) {
599 y = x - i - 11;
600 strncpy(dlbar, text, i);
601 dlbar[i] = 0;
602 strcat(dlbar, "...");
603 } else
604 strcpy(dlbar, text);
605 strcat(dlbar, ": ");
606 i = strlen(dlbar);
607 dlbar[i++] = '\x80';
608 // where's the dot go?
609 if (cls.downloadpercent == 0)
610 n = 0;
611 else
612 n = y * cls.downloadpercent / 100;
613
614 for (j = 0; j < y; j++)
615 if (j == n)
616 dlbar[i++] = '\x83';
617 else
618 dlbar[i++] = '\x81';
619 dlbar[i++] = '\x82';
620 dlbar[i] = 0;
621
622 sprintf(dlbar + strlen(dlbar), " %02d%%", cls.downloadpercent);
623
624 // draw it
625 y = con_vislines-22 + 8;
626 for (i = 0; i < (int) strlen(dlbar); i++)
627 Draw_Character ( (i+1)<<3, y, dlbar[i]);
628 }
629
630
631 // draw the input prompt, user text, and cursor if desired
632 Con_DrawInput ();
633 }
634
635
636 /*
637 ==================
638 Con_NotifyBox
639 ==================
640 */
Con_NotifyBox(char * text)641 void Con_NotifyBox (char *text)
642 {
643 double t1, t2;
644
645 // during startup for sound / cd warnings
646 Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n");
647
648 Con_Printf (text);
649
650 Con_Printf ("Press a key.\n");
651 Con_Printf("\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n");
652
653 key_count = -2; // wait for a key down and up
654 key_dest = key_console;
655
656 do
657 {
658 t1 = Sys_DoubleTime ();
659 SCR_UpdateScreen ();
660 Sys_SendKeyEvents ();
661 t2 = Sys_DoubleTime ();
662 realtime += t2-t1; // make the cursor blink
663 } while (key_count < 0);
664
665 Con_Printf ("\n");
666 key_dest = key_game;
667 realtime = 0; // put the cursor back to invisible
668 }
669
670
671 /*
672 ==================
673 Con_SafePrintf
674
675 Okay to call even when the screen can't be updated
676 ==================
677 */
Con_SafePrintf(char * fmt,...)678 void Con_SafePrintf (char *fmt, ...)
679 {
680 va_list argptr;
681 char msg[1024];
682 int temp;
683
684 va_start (argptr,fmt);
685 vsprintf (msg,fmt,argptr);
686 va_end (argptr);
687
688 temp = scr_disabled_for_loading;
689 scr_disabled_for_loading = true;
690 Con_Printf ("%s", msg);
691 scr_disabled_for_loading = temp;
692 }
693
694