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