Gigantic commit - dlight system rewritten
[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 #include <unistd.h>
27 #endif
28 #ifdef WIN32
29 #include <io.h>
30 #endif
31 #include <fcntl.h>
32 #include "quakedef.h"
33
34 int             con_linewidth;
35
36 float           con_cursorspeed = 4;
37
38 #define         CON_TEXTSIZE    16384
39
40 qboolean        con_forcedup;           // because no entities to refresh
41
42 int                     con_totallines;         // total lines in console scrollback
43 int                     con_backscroll;         // lines up from bottom to display
44 int                     con_current;            // where next message will be printed
45 int                     con_x;                          // offset in current line for next print
46 char            *con_text=0;
47
48 cvar_t          con_notifytime = {"con_notifytime","3"};                //seconds
49 cvar_t          logfile = {"logfile","0"};
50
51 #define NUM_CON_TIMES 4
52 float           con_times[NUM_CON_TIMES];       // realtime time the line was generated
53                                                                 // for transparent notify lines
54
55 int                     con_vislines;
56
57 qboolean        con_debuglog;
58
59 #define         MAXCMDLINE      256
60 extern  char    key_lines[32][MAXCMDLINE];
61 extern  int             edit_line;
62 extern  int             key_linepos;
63                 
64
65 qboolean        con_initialized;
66
67 int                     con_notifylines;                // scan lines to clear for notify lines
68
69 extern void M_Menu_Main_f (void);
70
71 /*
72 ================
73 Con_ToggleConsole_f
74 ================
75 */
76 void Con_ToggleConsole_f (void)
77 {
78         if (key_dest == key_console)
79         {
80                 if (cls.state == ca_connected)
81                 {
82                         key_dest = key_game;
83                         key_lines[edit_line][1] = 0;    // clear any typing
84                         key_linepos = 1;
85                 }
86                 else
87                 {
88                         M_Menu_Main_f ();
89                 }
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: don't print text while loading scripts
412         if (cls.state != ca_disconnected)
413         if (cls.signon != SIGNONS && !scr_disabled_for_loading )
414         {
415         // protect against infinite loop if something in SCR_UpdateScreen calls
416         // Con_Printf
417                 if (!inupdate)
418                 {
419                         inupdate = true;
420                         SCR_UpdateScreen ();
421                         inupdate = false;
422                 }
423         }
424 }
425
426 /*
427 ================
428 Con_DPrintf
429
430 A Con_Printf that only shows up if the "developer" cvar is set
431 ================
432 */
433 void Con_DPrintf (char *fmt, ...)
434 {
435         va_list         argptr;
436         char            msg[MAXPRINTMSG];
437                 
438         if (!developer.value)
439                 return;                 // don't confuse non-developers with techie stuff...
440
441         va_start (argptr,fmt);
442         vsprintf (msg,fmt,argptr);
443         va_end (argptr);
444         
445         Con_Printf ("%s", msg);
446 }
447
448
449 /*
450 ==================
451 Con_SafePrintf
452
453 Okay to call even when the screen can't be updated
454 ==================
455 */
456 void Con_SafePrintf (char *fmt, ...)
457 {
458         va_list         argptr;
459         char            msg[1024];
460         int                     temp;
461                 
462         va_start (argptr,fmt);
463         vsprintf (msg,fmt,argptr);
464         va_end (argptr);
465
466         temp = scr_disabled_for_loading;
467         scr_disabled_for_loading = true;
468         Con_Printf ("%s", msg);
469         scr_disabled_for_loading = temp;
470 }
471
472
473 /*
474 ==============================================================================
475
476 DRAWING
477
478 ==============================================================================
479 */
480
481
482 /*
483 ================
484 Con_DrawInput
485
486 The input line scrolls horizontally if typing goes beyond the right edge
487 ================
488 */
489 void Con_DrawInput (void)
490 {
491         int             y;
492         char    *text;
493
494         if (key_dest != key_console && !con_forcedup)
495                 return;         // don't draw anything
496
497         text = key_lines[edit_line];
498         
499 // add the cursor frame
500         text[key_linepos] = 10+((int)(realtime*con_cursorspeed)&1);
501         
502         text[key_linepos+1] = 0; // LordHavoc: null terminate, rather than padding with spaces
503 // fill out remainder with spaces
504 //      for (i=key_linepos+1 ; i< con_linewidth ; i++)
505 //              text[i] = ' ';
506                 
507 //      prestep if horizontally scrolling
508         if (key_linepos >= con_linewidth)
509                 text += 1 + key_linepos - con_linewidth;
510                 
511 // draw it
512         y = con_vislines-16;
513
514 //      for (i=0 ; i<con_linewidth ; i++)
515 //              Draw_Character ( (i+1)<<3, con_vislines - 16, text[i]);
516         // LordHavoc: speedup
517         Draw_String(8, con_vislines - 16, text, con_linewidth);
518
519 // remove cursor
520         key_lines[edit_line][key_linepos] = 0;
521 }
522
523
524 /*
525 ================
526 Con_DrawNotify
527
528 Draws the last few lines of output transparently over the game top
529 ================
530 */
531 void Con_DrawNotify (void)
532 {
533         int             x, v;
534         char    *text;
535         int             i;
536         float   time;
537         extern char chat_buffer[];
538         char    temptext[256];
539
540         v = 0;
541         for (i= con_current-NUM_CON_TIMES+1 ; i<=con_current ; i++)
542         {
543                 if (i < 0)
544                         continue;
545                 time = con_times[i % NUM_CON_TIMES];
546                 if (time == 0)
547                         continue;
548                 time = realtime - time;
549                 if (time > con_notifytime.value)
550                         continue;
551                 text = con_text + (i % con_totallines)*con_linewidth;
552                 
553                 clearnotify = 0;
554                 scr_copytop = 1;
555
556 //              for (x = 0 ; x < con_linewidth ; x++)
557 //                      Draw_Character ( (x+1)<<3, v, text[x]);
558                 // LordHavoc: speedup
559                 Draw_String(8, v, text, con_linewidth);
560
561                 v += 8;
562         }
563
564
565         if (key_dest == key_message)
566         {
567                 clearnotify = 0;
568                 scr_copytop = 1;
569         
570                 x = 0;
571                 
572                 // LordHavoc: speedup, and other improvements
573                 if (team_message)
574                         sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
575                 else
576                         sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
577                 while (strlen(temptext) >= con_linewidth)
578                 {
579                         Draw_String (8, v, temptext, con_linewidth);
580                         strcpy(temptext, &temptext[con_linewidth]);
581                         v += 8;
582                 }
583                 if (strlen(temptext) > 0)
584                 {
585                         Draw_String (8, v, temptext, 0);
586                         v += 8;
587                 }
588 //              Draw_String (8, v, "say:", 0);
589 //              while(chat_buffer[x])
590 //              {
591 //                      Draw_Character ( (x+5)<<3, v, chat_buffer[x]);
592 //                      x++;
593 //              }
594 //              Draw_Character ( (x+5)<<3, v, 10+((int)(realtime*con_cursorspeed)&1));
595 //              v += 8;
596         }
597         
598         if (v > con_notifylines)
599                 con_notifylines = v;
600 }
601
602 /*
603 ================
604 Con_DrawConsole
605
606 Draws the console with the solid background
607 The typing input line at the bottom should only be drawn if typing is allowed
608 ================
609 */
610 void Con_DrawConsole (int lines, qboolean drawinput)
611 {
612         int                             i, y;
613         int                             rows;
614         char                    *text;
615         int                             j;
616         
617         if (lines <= 0)
618                 return;
619
620 // draw the background
621         Draw_ConsoleBackground (lines);
622
623 // draw the text
624         con_vislines = lines;
625
626         rows = (lines-16)>>3;           // rows of text to draw
627         y = lines - 16 - (rows<<3);     // may start slightly negative
628
629         for (i= con_current - rows + 1 ; i<=con_current ; i++, y+=8 )
630         {
631                 j = i - con_backscroll;
632                 if (j<0)
633                         j = 0;
634                 text = con_text + (j % con_totallines)*con_linewidth;
635
636 //              for (x=0 ; x<con_linewidth ; x++)
637 //                      Draw_Character ( (x+1)<<3, y, text[x]);
638                 // LordHavoc: speedup
639                 Draw_String(8, y, text, con_linewidth);
640         }
641
642 // draw the input prompt, user text, and cursor if desired
643         if (drawinput)
644                 Con_DrawInput ();
645 }
646
647
648 /*
649 ==================
650 Con_NotifyBox
651 ==================
652 */
653 void Con_NotifyBox (char *text)
654 {
655         double          t1, t2;
656
657 // during startup for sound / cd warnings
658         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");
659
660         Con_Printf (text);
661
662         Con_Printf ("Press a key.\n");
663         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");
664
665         key_count = -2;         // wait for a key down and up
666         key_dest = key_console;
667
668         do
669         {
670                 t1 = Sys_FloatTime ();
671                 SCR_UpdateScreen ();
672                 Sys_SendKeyEvents ();
673                 t2 = Sys_FloatTime ();
674                 realtime += t2-t1;              // make the cursor blink
675         } while (key_count < 0);
676
677         Con_Printf ("\n");
678         key_dest = key_game;
679         realtime = 0;                           // put the cursor back to invisible
680 }
681