c5332a9931c76cdbf7cd371eed190e021ea93bf7
[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 = {CVAR_SAVE, "con_notifytime","3"};     //seconds
51 cvar_t          logfile = {0, "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 extern  int             key_insert;
66
67
68 qboolean        con_initialized;
69
70 mempool_t       *console_mempool;
71
72 int             con_notifylines;                // scan lines to clear for notify lines
73
74 extern void M_Menu_Main_f (void);
75
76 /*
77 ================
78 Con_ToggleConsole_f
79 ================
80 */
81 void Con_ToggleConsole_f (void)
82 {
83         if (key_dest == key_console)
84         {
85                 if (cls.state == ca_connected)
86 //              {
87                         key_dest = key_game;
88 //                      key_lines[edit_line][1] = 0;    // clear any typing
89 //                      key_linepos = 1;
90 //              }
91                 else
92                         M_Menu_Main_f ();
93         }
94         else
95                 key_dest = key_console;
96         
97 //      SCR_EndLoadingPlaque ();
98         memset (con_times, 0, sizeof(con_times));
99 }
100
101 /*
102 ================
103 Con_Clear_f
104 ================
105 */
106 void Con_Clear_f (void)
107 {
108         if (con_text)
109                 memset (con_text, ' ', CON_TEXTSIZE);
110 }
111
112                                                 
113 /*
114 ================
115 Con_ClearNotify
116 ================
117 */
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 */
132 extern qboolean team_message;
133
134 void Con_MessageMode_f (void)
135 {
136         key_dest = key_message;
137         team_message = false;
138 }
139
140                                                 
141 /*
142 ================
143 Con_MessageMode2_f
144 ================
145 */
146 void Con_MessageMode2_f (void)
147 {
148         key_dest = key_message;
149         team_message = true;
150 }
151
152                                                 
153 /*
154 ================
155 Con_CheckResize
156
157 If the line width has changed, reformat the buffer.
158 ================
159 */
160 void Con_CheckResize (void)
161 {
162         int             i, j, width, oldwidth, oldtotallines, numlines, numchars;
163         char    tbuf[CON_TEXTSIZE];
164
165         width = (vid.conwidth >> 3) - 2;
166
167         if (width == con_linewidth)
168                 return;
169
170         if (width < 1)                  // video hasn't been initialized yet
171         {
172                 width = 78; // LordHavoc: changed from 38 to 78 (320 -> 640 conversion)
173                 con_linewidth = width;
174                 con_totallines = CON_TEXTSIZE / con_linewidth;
175                 memset (con_text, ' ', CON_TEXTSIZE);
176         }
177         else
178         {
179                 oldwidth = con_linewidth;
180                 con_linewidth = width;
181                 oldtotallines = con_totallines;
182                 con_totallines = CON_TEXTSIZE / con_linewidth;
183                 numlines = oldtotallines;
184
185                 if (con_totallines < numlines)
186                         numlines = con_totallines;
187
188                 numchars = oldwidth;
189         
190                 if (con_linewidth < numchars)
191                         numchars = con_linewidth;
192
193                 memcpy (tbuf, con_text, CON_TEXTSIZE);
194                 memset (con_text, ' ', CON_TEXTSIZE);
195
196                 for (i=0 ; i<numlines ; i++)
197                 {
198                         for (j=0 ; j<numchars ; j++)
199                         {
200                                 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
201                                                 tbuf[((con_current - i + oldtotallines) %
202                                                           oldtotallines) * oldwidth + j];
203                         }
204                 }
205
206                 Con_ClearNotify ();
207         }
208
209         con_backscroll = 0;
210         con_current = con_totallines - 1;
211 }
212
213
214 /*
215 ================
216 Con_Init
217 ================
218 */
219 void Con_Init (void)
220 {
221 #define MAXGAMEDIRLEN   1000
222         char    temp[MAXGAMEDIRLEN+1];
223         char    *t2 = "/qconsole.log";
224
225         Cvar_RegisterVariable(&logfile);
226         con_debuglog = COM_CheckParm("-condebug");
227
228         if (con_debuglog)
229         {
230                 if (strlen (com_gamedir) < (MAXGAMEDIRLEN - strlen (t2)))
231                 {
232                         sprintf (temp, "%s%s", com_gamedir, t2);
233                         unlink (temp);
234                 }
235                 logfile.integer = 1;
236         }
237
238         console_mempool = Mem_AllocPool("console");
239         con_text = Mem_Alloc(console_mempool, CON_TEXTSIZE);
240         memset (con_text, ' ', CON_TEXTSIZE);
241         con_linewidth = -1;
242         Con_CheckResize ();
243
244         Con_Printf ("Console initialized.\n");
245
246 //
247 // register our commands
248 //
249         Cvar_RegisterVariable (&con_notifytime);
250
251         Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
252         Cmd_AddCommand ("messagemode", Con_MessageMode_f);
253         Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
254         Cmd_AddCommand ("clear", Con_Clear_f);
255         con_initialized = true;
256 }
257
258
259 /*
260 ===============
261 Con_Linefeed
262 ===============
263 */
264 void Con_Linefeed (void)
265 {
266         con_x = 0;
267         con_current++;
268         memset (&con_text[(con_current%con_totallines)*con_linewidth]
269         , ' ', con_linewidth);
270 }
271
272 /*
273 ================
274 Con_Print
275
276 Handles cursor positioning, line wrapping, etc
277 All console printing must go through this in order to be logged to disk
278 If no console is visible, the notify window will pop up.
279 ================
280 */
281 void Con_Print (char *txt)
282 {
283         int             y;
284         int             c, l;
285         static int      cr;
286         int             mask;
287
288         con_backscroll = 0;
289
290         if (txt[0] == 1)
291         {
292                 mask = 128;             // go to colored text
293                 S_LocalSound ("misc/talk.wav");
294         // play talk wav
295                 txt++;
296         }
297         else if (txt[0] == 2)
298         {
299                 mask = 128;             // go to colored text
300                 txt++;
301         }
302         else
303                 mask = 0;
304
305
306         while ( (c = *txt) )
307         {
308         // count word length
309                 for (l=0 ; l< con_linewidth ; l++)
310                         if ( txt[l] <= ' ')
311                                 break;
312
313         // word wrap
314                 if (l != con_linewidth && (con_x + l > con_linewidth) )
315                         con_x = 0;
316
317                 txt++;
318
319                 if (cr)
320                 {
321                         con_current--;
322                         cr = false;
323                 }
324
325                 
326                 if (!con_x)
327                 {
328                         Con_Linefeed ();
329                 // mark time for transparent overlay
330                         if (con_current >= 0)
331                                 con_times[con_current % NUM_CON_TIMES] = realtime;
332                 }
333
334                 switch (c)
335                 {
336                 case '\n':
337                         con_x = 0;
338                         break;
339
340                 case '\r':
341                         con_x = 0;
342                         cr = 1;
343                         break;
344
345                 default:        // display character and advance
346                         y = con_current % con_totallines;
347                         con_text[y*con_linewidth+con_x] = c | mask;
348                         con_x++;
349                         if (con_x >= con_linewidth)
350                                 con_x = 0;
351                         break;
352                 }
353                 
354         }
355 }
356
357
358 /*
359 ================
360 Con_DebugLog
361 ================
362 */
363 void Con_DebugLog(char *file, char *fmt, ...)
364 {
365     va_list argptr; 
366     static char data[1024];
367     int fd;
368     
369     va_start(argptr, fmt);
370     vsprintf(data, fmt, argptr);
371     va_end(argptr);
372     fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666);
373     write(fd, data, strlen(data));
374     close(fd);
375 }
376
377
378 /*
379 ================
380 Con_Printf
381
382 Handles cursor positioning, line wrapping, etc
383 ================
384 */
385 // LordHavoc: increased from 4096 to 16384
386 #define MAXPRINTMSG     16384
387 // FIXME: make a buffer size safe vsprintf?
388 void Con_Printf (char *fmt, ...)
389 {
390         va_list         argptr;
391         char            msg[MAXPRINTMSG];
392 //      static qboolean inupdate;
393
394         va_start (argptr,fmt);
395         vsprintf (msg,fmt,argptr);
396         va_end (argptr);
397
398 // also echo to debugging console
399         Sys_Printf ("%s", msg);
400
401 // log all messages to file
402         if (con_debuglog)
403         {
404                 // can't use va() here because it might overwrite other important things
405                 char logname[MAX_OSPATH];
406                 sprintf(logname, "%s/qconsole.log", com_gamedir);
407                 Con_DebugLog(logname, "%s", msg);
408         }
409
410         if (!con_initialized)
411                 return;
412
413         if (cls.state == ca_dedicated)
414                 return;         // no graphics mode
415
416 // write it to the scrollable buffer
417         Con_Print (msg);
418
419 // update the screen if the console is displayed
420         // LordHavoc: I don't think there's a real need for this
421         /*
422         // LordHavoc: don't print text while loading scripts
423         if (cls.state != ca_disconnected)
424         if (cls.signon != SIGNONS && !scr_disabled_for_loading )
425         {
426         // protect against infinite loop if something in SCR_UpdateScreen calls
427         // Con_Printf
428                 if (!inupdate)
429                 {
430                         inupdate = true;
431                         SCR_UpdateScreen ();
432                         inupdate = false;
433                 }
434         }
435         */
436 }
437
438 /*
439 ================
440 Con_DPrintf
441
442 A Con_Printf that only shows up if the "developer" cvar is set
443 ================
444 */
445 void Con_DPrintf (char *fmt, ...)
446 {
447         va_list         argptr;
448         char            msg[MAXPRINTMSG];
449
450         if (!developer.integer)
451                 return;                 // don't confuse non-developers with techie stuff...
452
453         va_start (argptr,fmt);
454         vsprintf (msg,fmt,argptr);
455         va_end (argptr);
456         
457         Con_Printf ("%s", msg);
458 }
459
460
461 /*
462 ==================
463 Con_SafePrintf
464
465 Okay to call even when the screen can't be updated
466 ==================
467 */
468 void Con_SafePrintf (char *fmt, ...)
469 {
470         va_list         argptr;
471         char            msg[1024];
472         int                     temp;
473                 
474         va_start (argptr,fmt);
475         vsprintf (msg,fmt,argptr);
476         va_end (argptr);
477
478         temp = scr_disabled_for_loading;
479         scr_disabled_for_loading = true;
480         Con_Printf ("%s", msg);
481         scr_disabled_for_loading = temp;
482 }
483
484
485 /*
486 ==============================================================================
487
488 DRAWING
489
490 ==============================================================================
491 */
492
493
494 /*
495 ================
496 Con_DrawInput
497
498 The input line scrolls horizontally if typing goes beyond the right edge
499
500 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
501 ================
502 */
503 void Con_DrawInput (void)
504 {
505         char editlinecopy[256], *text;
506
507         if (key_dest != key_console && !con_forcedup)
508                 return;         // don't draw anything
509
510         text = strcpy(editlinecopy, key_lines[edit_line]);
511
512         // Advanced Console Editing by Radix radix@planetquake.com
513         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
514         // use strlen of edit_line instead of key_linepos to allow editing
515         // of early characters w/o erasing
516
517         // add the cursor frame
518         if ((int)(realtime*con_cursorspeed) & 1)                // cursor is visible
519                 text[key_linepos] = 11 + 130 * key_insert;      // either solid or triangle facing right
520
521         text[key_linepos + 1] = 0;
522
523         // prestep if horizontally scrolling
524         if (key_linepos >= con_linewidth)
525                 text += 1 + key_linepos - con_linewidth;
526
527         // draw it
528         DrawQ_String(8, con_vislines - 16, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
529
530         // remove cursor
531         key_lines[edit_line][key_linepos] = 0;
532 }
533
534
535 /*
536 ================
537 Con_DrawNotify
538
539 Draws the last few lines of output transparently over the game top
540 ================
541 */
542 void Con_DrawNotify (void)
543 {
544         int             x, v;
545         char    *text;
546         int             i;
547         float   time;
548         extern char chat_buffer[];
549         char    temptext[256];
550
551         v = 0;
552         for (i= con_current-NUM_CON_TIMES+1 ; i<=con_current ; i++)
553         {
554                 if (i < 0)
555                         continue;
556                 time = con_times[i % NUM_CON_TIMES];
557                 if (time == 0)
558                         continue;
559                 time = realtime - time;
560                 if (time > con_notifytime.value)
561                         continue;
562                 text = con_text + (i % con_totallines)*con_linewidth;
563
564                 clearnotify = 0;
565
566                 DrawQ_String(8, v, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
567
568                 v += 8;
569         }
570
571
572         if (key_dest == key_message)
573         {
574                 clearnotify = 0;
575
576                 x = 0;
577
578                 // LordHavoc: speedup, and other improvements
579                 if (team_message)
580                         sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
581                 else
582                         sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
583                 while (strlen(temptext) >= con_linewidth)
584                 {
585                         DrawQ_String (8, v, temptext, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
586                         strcpy(temptext, &temptext[con_linewidth]);
587                         v += 8;
588                 }
589                 if (strlen(temptext) > 0)
590                 {
591                         DrawQ_String (8, v, temptext, 0, 8, 8, 1, 1, 1, 1, 0);
592                         v += 8;
593                 }
594         }
595
596         if (v > con_notifylines)
597                 con_notifylines = v;
598 }
599
600 /*
601 ================
602 Con_DrawConsole
603
604 Draws the console with the solid background
605 The typing input line at the bottom should only be drawn if typing is allowed
606 ================
607 */
608 extern cvar_t scr_conalpha;
609 extern char engineversion[40];
610 void Con_DrawConsole (int lines)
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         DrawQ_Pic(0, lines - vid.conheight, "gfx/conback", vid.conwidth, vid.conheight, 1, 1, 1, scr_conalpha.value * lines / vid.conheight, 0);
622         DrawQ_String(vid.conwidth - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
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 = max(i - con_backscroll, 0);
633                 text = con_text + (j % con_totallines)*con_linewidth;
634
635                 DrawQ_String(8, y, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
636         }
637
638 // draw the input prompt, user text, and cursor if desired
639         Con_DrawInput ();
640 }
641
642 /*
643         Con_DisplayList
644
645         New function for tab-completion system
646         Added by EvilTypeGuy
647         MEGA Thanks to Taniwha
648
649 */
650 void
651 Con_DisplayList(char **list)
652 {
653         int     i = 0;
654         int     pos = 0;
655         int     len = 0;
656         int     maxlen = 0;
657         int     width = (con_linewidth - 4);
658         char    **walk = list;
659
660         while (*walk) {
661                 len = strlen(*walk);
662                 if (len > maxlen)
663                         maxlen = len;
664                 walk++;
665         }
666         maxlen += 1;
667
668         while (*list) {
669                 len = strlen(*list);
670                 if (pos + maxlen >= width) {
671                         Con_Printf("\n");
672                         pos = 0;
673                 }
674
675                 Con_Printf("%s", *list);
676                 for (i = 0; i < (maxlen - len); i++)
677                         Con_Printf(" ");
678
679                 pos += maxlen;
680                 list++;
681         }
682
683         if (pos)
684                 Con_Printf("\n\n");
685 }
686
687 /*
688         Con_CompleteCommandLine
689
690         New function for tab-completion system
691         Added by EvilTypeGuy
692         Thanks to Fett erich@heintz.com
693         Thanks to taniwha
694
695 */
696 void
697 Con_CompleteCommandLine (void)
698 {
699         char    *cmd = "";
700         char    *s;
701         int             c, v, a, i;
702         int             cmd_len;
703         char    **list[3] = {0, 0, 0};
704
705         s = key_lines[edit_line] + 1;
706         // Count number of possible matches
707         c = Cmd_CompleteCountPossible(s);
708         v = Cvar_CompleteCountPossible(s);
709         a = Cmd_CompleteAliasCountPossible(s);
710         
711         if (!(c + v + a))       // No possible matches
712                 return;
713         
714         if (c + v + a == 1) {
715                 if (c)
716                         list[0] = Cmd_CompleteBuildList(s);
717                 else if (v)
718                         list[0] = Cvar_CompleteBuildList(s);
719                 else
720                         list[0] = Cmd_CompleteAliasBuildList(s);
721                 cmd = *list[0];
722                 cmd_len = strlen (cmd);
723         } else {
724                 if (c)
725                         cmd = *(list[0] = Cmd_CompleteBuildList(s));
726                 if (v)
727                         cmd = *(list[1] = Cvar_CompleteBuildList(s));
728                 if (a)
729                         cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
730
731                 cmd_len = strlen (s);
732                 do {
733                         for (i = 0; i < 3; i++) {
734                                 char ch = cmd[cmd_len];
735                                 char **l = list[i];
736                                 if (l) {
737                                         while (*l && (*l)[cmd_len] == ch)
738                                                 l++;
739                                         if (*l)
740                                                 break;
741                                 }
742                         }
743                         if (i == 3)
744                                 cmd_len++;
745                 } while (i == 3);
746                 // 'quakebar'
747                 Con_Printf("\n\35");
748                 for (i = 0; i < con_linewidth - 4; i++)
749                         Con_Printf("\36");
750                 Con_Printf("\37\n");
751
752                 // Print Possible Commands
753                 if (c) {
754                         Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
755                         Con_DisplayList(list[0]);
756                 }
757                 
758                 if (v) {
759                         Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
760                         Con_DisplayList(list[1]);
761                 }
762                 
763                 if (a) {
764                         Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
765                         Con_DisplayList(list[2]);
766                 }
767         }
768         
769         if (cmd) {
770                 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
771                 key_linepos = cmd_len + 1;
772                 if (c + v + a == 1) {
773                         key_lines[edit_line][key_linepos] = ' ';
774                         key_linepos++;
775                 }
776                 key_lines[edit_line][key_linepos] = 0;
777         }
778         for (i = 0; i < 3; i++)
779                 if (list[i])
780                         Mem_Free(list[i]);
781 }
782