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