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