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