cleaned up nearly all of the externs in .c files (moved to appropriate .h files)
[divverent/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                         M_Menu_Main_f ();
90         }
91         else
92                 key_dest = key_console;
93         
94 //      SCR_EndLoadingPlaque ();
95         memset (con_times, 0, sizeof(con_times));
96 }
97
98 /*
99 ================
100 Con_Clear_f
101 ================
102 */
103 void Con_Clear_f (void)
104 {
105         if (con_text)
106                 memset (con_text, ' ', CON_TEXTSIZE);
107 }
108
109                                                 
110 /*
111 ================
112 Con_ClearNotify
113 ================
114 */
115 void Con_ClearNotify (void)
116 {
117         int             i;
118         
119         for (i=0 ; i<NUM_CON_TIMES ; i++)
120                 con_times[i] = 0;
121 }
122
123                                                 
124 /*
125 ================
126 Con_MessageMode_f
127 ================
128 */
129 extern qboolean team_message;
130
131 void Con_MessageMode_f (void)
132 {
133         key_dest = key_message;
134         team_message = false;
135 }
136
137                                                 
138 /*
139 ================
140 Con_MessageMode2_f
141 ================
142 */
143 void Con_MessageMode2_f (void)
144 {
145         key_dest = key_message;
146         team_message = true;
147 }
148
149                                                 
150 /*
151 ================
152 Con_CheckResize
153
154 If the line width has changed, reformat the buffer.
155 ================
156 */
157 void Con_CheckResize (void)
158 {
159         int             i, j, width, oldwidth, oldtotallines, numlines, numchars;
160         char    tbuf[CON_TEXTSIZE];
161
162         width = (vid.width >> 3) - 2;
163
164         if (width == con_linewidth)
165                 return;
166
167         if (width < 1)                  // video hasn't been initialized yet
168         {
169                 width = 78; // LordHavoc: changed from 38 to 78 (320 -> 640 conversion)
170                 con_linewidth = width;
171                 con_totallines = CON_TEXTSIZE / con_linewidth;
172                 memset (con_text, ' ', CON_TEXTSIZE);
173         }
174         else
175         {
176                 oldwidth = con_linewidth;
177                 con_linewidth = width;
178                 oldtotallines = con_totallines;
179                 con_totallines = CON_TEXTSIZE / con_linewidth;
180                 numlines = oldtotallines;
181
182                 if (con_totallines < numlines)
183                         numlines = con_totallines;
184
185                 numchars = oldwidth;
186         
187                 if (con_linewidth < numchars)
188                         numchars = con_linewidth;
189
190                 memcpy (tbuf, con_text, CON_TEXTSIZE);
191                 memset (con_text, ' ', CON_TEXTSIZE);
192
193                 for (i=0 ; i<numlines ; i++)
194                 {
195                         for (j=0 ; j<numchars ; j++)
196                         {
197                                 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
198                                                 tbuf[((con_current - i + oldtotallines) %
199                                                           oldtotallines) * oldwidth + j];
200                         }
201                 }
202
203                 Con_ClearNotify ();
204         }
205
206         con_backscroll = 0;
207         con_current = con_totallines - 1;
208 }
209
210
211 /*
212 ================
213 Con_Init
214 ================
215 */
216 void Con_Init (void)
217 {
218 #define MAXGAMEDIRLEN   1000
219         char    temp[MAXGAMEDIRLEN+1];
220         char    *t2 = "/qconsole.log";
221
222         Cvar_RegisterVariable(&logfile);
223         con_debuglog = COM_CheckParm("-condebug");
224
225         if (con_debuglog)
226         {
227                 if (strlen (com_gamedir) < (MAXGAMEDIRLEN - strlen (t2)))
228                 {
229                         sprintf (temp, "%s%s", com_gamedir, t2);
230                         unlink (temp);
231                 }
232                 logfile.value = 1;
233         }
234
235         con_text = Hunk_AllocName (CON_TEXTSIZE, "context");
236         memset (con_text, ' ', CON_TEXTSIZE);
237         con_linewidth = -1;
238         Con_CheckResize ();
239         
240         Con_Printf ("Console initialized.\n");
241
242 //
243 // register our commands
244 //
245         Cvar_RegisterVariable (&con_notifytime);
246
247         Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
248         Cmd_AddCommand ("messagemode", Con_MessageMode_f);
249         Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
250         Cmd_AddCommand ("clear", Con_Clear_f);
251         con_initialized = true;
252 }
253
254
255 /*
256 ===============
257 Con_Linefeed
258 ===============
259 */
260 void Con_Linefeed (void)
261 {
262         con_x = 0;
263         con_current++;
264         memset (&con_text[(con_current%con_totallines)*con_linewidth]
265         , ' ', con_linewidth);
266 }
267
268 /*
269 ================
270 Con_Print
271
272 Handles cursor positioning, line wrapping, etc
273 All console printing must go through this in order to be logged to disk
274 If no console is visible, the notify window will pop up.
275 ================
276 */
277 void Con_Print (char *txt)
278 {
279         int             y;
280         int             c, l;
281         static int      cr;
282         int             mask;
283         
284         con_backscroll = 0;
285
286         if (txt[0] == 1)
287         {
288                 mask = 128;             // go to colored text
289                 S_LocalSound ("misc/talk.wav");
290         // play talk wav
291                 txt++;
292         }
293         else if (txt[0] == 2)
294         {
295                 mask = 128;             // go to colored text
296                 txt++;
297         }
298         else
299                 mask = 0;
300
301
302         while ( (c = *txt) )
303         {
304         // count word length
305                 for (l=0 ; l< con_linewidth ; l++)
306                         if ( txt[l] <= ' ')
307                                 break;
308
309         // word wrap
310                 if (l != con_linewidth && (con_x + l > con_linewidth) )
311                         con_x = 0;
312
313                 txt++;
314
315                 if (cr)
316                 {
317                         con_current--;
318                         cr = false;
319                 }
320
321                 
322                 if (!con_x)
323                 {
324                         Con_Linefeed ();
325                 // mark time for transparent overlay
326                         if (con_current >= 0)
327                                 con_times[con_current % NUM_CON_TIMES] = realtime;
328                 }
329
330                 switch (c)
331                 {
332                 case '\n':
333                         con_x = 0;
334                         break;
335
336                 case '\r':
337                         con_x = 0;
338                         cr = 1;
339                         break;
340
341                 default:        // display character and advance
342                         y = con_current % con_totallines;
343                         con_text[y*con_linewidth+con_x] = c | mask;
344                         con_x++;
345                         if (con_x >= con_linewidth)
346                                 con_x = 0;
347                         break;
348                 }
349                 
350         }
351 }
352
353
354 /*
355 ================
356 Con_DebugLog
357 ================
358 */
359 void Con_DebugLog(char *file, char *fmt, ...)
360 {
361     va_list argptr; 
362     static char data[1024];
363     int fd;
364     
365     va_start(argptr, fmt);
366     vsprintf(data, fmt, argptr);
367     va_end(argptr);
368     fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666);
369     write(fd, data, strlen(data));
370     close(fd);
371 }
372
373
374 /*
375 ================
376 Con_Printf
377
378 Handles cursor positioning, line wrapping, etc
379 ================
380 */
381 // LordHavoc: increased from 4096 to 16384
382 #define MAXPRINTMSG     16384
383 // FIXME: make a buffer size safe vsprintf?
384 void Con_Printf (char *fmt, ...)
385 {
386         va_list         argptr;
387         char            msg[MAXPRINTMSG];
388 //      static qboolean inupdate;
389         
390         va_start (argptr,fmt);
391         vsprintf (msg,fmt,argptr);
392         va_end (argptr);
393         
394 // also echo to debugging console
395         Sys_Printf ("%s", msg); // also echo to debugging console
396
397 // log all messages to file
398         if (con_debuglog)
399                 Con_DebugLog(va("%s/qconsole.log",com_gamedir), "%s", msg);
400
401         if (!con_initialized)
402                 return;
403                 
404         if (cls.state == ca_dedicated)
405                 return;         // no graphics mode
406
407 // write it to the scrollable buffer
408         Con_Print (msg);
409         
410 // update the screen if the console is displayed
411         // LordHavoc: I don't think there's a real need for this
412         /*
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 ================
431 Con_DPrintf
432
433 A Con_Printf that only shows up if the "developer" cvar is set
434 ================
435 */
436 void Con_DPrintf (char *fmt, ...)
437 {
438         va_list         argptr;
439         char            msg[MAXPRINTMSG];
440                 
441         if (!developer.value)
442                 return;                 // don't confuse non-developers with techie stuff...
443
444         va_start (argptr,fmt);
445         vsprintf (msg,fmt,argptr);
446         va_end (argptr);
447         
448         Con_Printf ("%s", msg);
449 }
450
451
452 /*
453 ==================
454 Con_SafePrintf
455
456 Okay to call even when the screen can't be updated
457 ==================
458 */
459 void Con_SafePrintf (char *fmt, ...)
460 {
461         va_list         argptr;
462         char            msg[1024];
463         int                     temp;
464                 
465         va_start (argptr,fmt);
466         vsprintf (msg,fmt,argptr);
467         va_end (argptr);
468
469         temp = scr_disabled_for_loading;
470         scr_disabled_for_loading = true;
471         Con_Printf ("%s", msg);
472         scr_disabled_for_loading = temp;
473 }
474
475
476 /*
477 ==============================================================================
478
479 DRAWING
480
481 ==============================================================================
482 */
483
484
485 /*
486 ================
487 Con_DrawInput
488
489 The input line scrolls horizontally if typing goes beyond the right edge
490 ================
491 */
492 void Con_DrawInput (void)
493 {
494         int             y;
495         char    *text;
496
497         if (key_dest != key_console && !con_forcedup)
498                 return;         // don't draw anything
499
500         text = key_lines[edit_line];
501         
502 // add the cursor frame
503         text[key_linepos] = 10+((int)(realtime*con_cursorspeed)&1);
504         
505         text[key_linepos+1] = 0; // LordHavoc: null terminate, rather than padding with spaces
506 // fill out remainder with spaces
507 //      for (i=key_linepos+1 ; i< con_linewidth ; i++)
508 //              text[i] = ' ';
509                 
510 //      prestep if horizontally scrolling
511         if (key_linepos >= con_linewidth)
512                 text += 1 + key_linepos - con_linewidth;
513                 
514 // draw it
515         y = con_vislines-16;
516
517 //      for (i=0 ; i<con_linewidth ; i++)
518 //              Draw_Character ( (i+1)<<3, con_vislines - 16, text[i]);
519         // LordHavoc: speedup
520         Draw_String(8, con_vislines - 16, text, con_linewidth);
521
522 // remove cursor
523         key_lines[edit_line][key_linepos] = 0;
524 }
525
526
527 /*
528 ================
529 Con_DrawNotify
530
531 Draws the last few lines of output transparently over the game top
532 ================
533 */
534 void Con_DrawNotify (void)
535 {
536         int             x, v;
537         char    *text;
538         int             i;
539         float   time;
540         extern char chat_buffer[];
541         char    temptext[256];
542
543         v = 0;
544         for (i= con_current-NUM_CON_TIMES+1 ; i<=con_current ; i++)
545         {
546                 if (i < 0)
547                         continue;
548                 time = con_times[i % NUM_CON_TIMES];
549                 if (time == 0)
550                         continue;
551                 time = realtime - time;
552                 if (time > con_notifytime.value)
553                         continue;
554                 text = con_text + (i % con_totallines)*con_linewidth;
555                 
556                 clearnotify = 0;
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         
571                 x = 0;
572                 
573                 // LordHavoc: speedup, and other improvements
574                 if (team_message)
575                         sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
576                 else
577                         sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
578                 while (strlen(temptext) >= con_linewidth)
579                 {
580                         Draw_String (8, v, temptext, con_linewidth);
581                         strcpy(temptext, &temptext[con_linewidth]);
582                         v += 8;
583                 }
584                 if (strlen(temptext) > 0)
585                 {
586                         Draw_String (8, v, temptext, 0);
587                         v += 8;
588                 }
589 //              Draw_String (8, v, "say:", 0);
590 //              while(chat_buffer[x])
591 //              {
592 //                      Draw_Character ( (x+5)<<3, v, chat_buffer[x]);
593 //                      x++;
594 //              }
595 //              Draw_Character ( (x+5)<<3, v, 10+((int)(realtime*con_cursorspeed)&1));
596 //              v += 8;
597         }
598         
599         if (v > con_notifylines)
600                 con_notifylines = v;
601 }
602
603 /*
604 ================
605 Con_DrawConsole
606
607 Draws the console with the solid background
608 The typing input line at the bottom should only be drawn if typing is allowed
609 ================
610 */
611 void Con_DrawConsole (int lines, qboolean drawinput)
612 {
613         int                             i, y;
614         int                             rows;
615         char                    *text;
616         int                             j;
617         
618         if (lines <= 0)
619                 return;
620
621 // draw the background
622         Draw_ConsoleBackground (lines);
623
624 // draw the text
625         con_vislines = lines;
626
627         rows = (lines-16)>>3;           // rows of text to draw
628         y = lines - 16 - (rows<<3);     // may start slightly negative
629
630         for (i= con_current - rows + 1 ; i<=con_current ; i++, y+=8 )
631         {
632                 j = i - con_backscroll;
633                 if (j<0)
634                         j = 0;
635                 text = con_text + (j % con_totallines)*con_linewidth;
636
637 //              for (x=0 ; x<con_linewidth ; x++)
638 //                      Draw_Character ( (x+1)<<3, y, text[x]);
639                 // LordHavoc: speedup
640                 Draw_String(8, y, text, con_linewidth);
641         }
642
643 // draw the input prompt, user text, and cursor if desired
644         if (drawinput)
645                 Con_DrawInput ();
646 }