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