This is a patch from Elric greatly cleaning up the filesystem portions of the engine...
[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    131072
41
42 // total lines in console scrollback
43 int con_totallines;
44 // lines up from bottom to display
45 int con_backscroll;
46 // where next message will be printed
47 int con_current;
48 // offset in current line for next print
49 int con_x;
50 char *con_text = 0;
51
52 //seconds
53 cvar_t con_notifytime = {CVAR_SAVE, "con_notifytime","3"};
54 cvar_t con_notify = {CVAR_SAVE, "con_notify","4"};
55 cvar_t logfile = {0, "logfile","0"};
56
57 #define MAX_NOTIFYLINES 32
58 // realtime time the line was generated for transparent notify lines
59 float con_times[MAX_NOTIFYLINES];
60
61 int con_vislines;
62
63 qboolean con_debuglog;
64
65 #define MAXCMDLINE      256
66 extern char key_lines[32][MAXCMDLINE];
67 extern int edit_line;
68 extern int key_linepos;
69 extern int key_insert;
70
71
72 qboolean con_initialized;
73
74 mempool_t *console_mempool;
75
76 // scan lines to clear for notify lines
77 int con_notifylines;
78
79 extern void M_Menu_Main_f (void);
80
81 /*
82 ================
83 Con_ToggleConsole_f
84 ================
85 */
86 void Con_ToggleConsole_f (void)
87 {
88         // toggle the 'user wants console' bit
89         key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
90         memset (con_times, 0, sizeof(con_times));
91 }
92
93 /*
94 ================
95 Con_Clear_f
96 ================
97 */
98 void Con_Clear_f (void)
99 {
100         if (con_text)
101                 memset (con_text, ' ', CON_TEXTSIZE);
102 }
103
104
105 /*
106 ================
107 Con_ClearNotify
108 ================
109 */
110 void Con_ClearNotify (void)
111 {
112         int i;
113
114         for (i=0 ; i<MAX_NOTIFYLINES ; i++)
115                 con_times[i] = 0;
116 }
117
118
119 /*
120 ================
121 Con_MessageMode_f
122 ================
123 */
124 void Con_MessageMode_f (void)
125 {
126         key_dest = key_message;
127         chat_team = false;
128 }
129
130
131 /*
132 ================
133 Con_MessageMode2_f
134 ================
135 */
136 void Con_MessageMode2_f (void)
137 {
138         key_dest = key_message;
139         chat_team = true;
140 }
141
142
143 /*
144 ================
145 Con_CheckResize
146
147 If the line width has changed, reformat the buffer.
148 ================
149 */
150 void Con_CheckResize (void)
151 {
152         int i, j, width, oldwidth, oldtotallines, numlines, numchars;
153         char tbuf[CON_TEXTSIZE];
154
155         width = (vid.conwidth >> 3);
156
157         if (width == con_linewidth)
158                 return;
159
160         if (width < 1)                  // video hasn't been initialized yet
161         {
162                 width = 80;
163                 con_linewidth = width;
164                 con_totallines = CON_TEXTSIZE / con_linewidth;
165                 memset (con_text, ' ', CON_TEXTSIZE);
166         }
167         else
168         {
169                 oldwidth = con_linewidth;
170                 con_linewidth = width;
171                 oldtotallines = con_totallines;
172                 con_totallines = CON_TEXTSIZE / con_linewidth;
173                 numlines = oldtotallines;
174
175                 if (con_totallines < numlines)
176                         numlines = con_totallines;
177
178                 numchars = oldwidth;
179
180                 if (con_linewidth < numchars)
181                         numchars = con_linewidth;
182
183                 memcpy (tbuf, con_text, CON_TEXTSIZE);
184                 memset (con_text, ' ', CON_TEXTSIZE);
185
186                 for (i=0 ; i<numlines ; i++)
187                 {
188                         for (j=0 ; j<numchars ; j++)
189                         {
190                                 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
191                                                 tbuf[((con_current - i + oldtotallines) %
192                                                           oldtotallines) * oldwidth + j];
193                         }
194                 }
195
196                 Con_ClearNotify ();
197         }
198
199         con_backscroll = 0;
200         con_current = con_totallines - 1;
201 }
202
203
204 /*
205 ================
206 Con_Init
207 ================
208 */
209 void Con_Init (void)
210 {
211 #define MAXGAMEDIRLEN 1000
212         char temp[MAXGAMEDIRLEN+1];
213         char *t2 = "/qconsole.log";
214
215         Cvar_RegisterVariable(&logfile);
216         con_debuglog = COM_CheckParm("-condebug");
217
218         if (con_debuglog)
219         {
220                 if (strlen (fs_gamedir) < (MAXGAMEDIRLEN - strlen (t2)))
221                 {
222                         sprintf (temp, "%s%s", fs_gamedir, t2);
223                         unlink (temp);
224                 }
225                 logfile.integer = 1;
226         }
227
228         console_mempool = Mem_AllocPool("console");
229         con_text = Mem_Alloc(console_mempool, CON_TEXTSIZE);
230         memset (con_text, ' ', CON_TEXTSIZE);
231         con_linewidth = -1;
232         Con_CheckResize ();
233
234         Con_Printf ("Console initialized.\n");
235
236 //
237 // register our commands
238 //
239         Cvar_RegisterVariable (&con_notifytime);
240         Cvar_RegisterVariable (&con_notify);
241
242         Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
243         Cmd_AddCommand ("messagemode", Con_MessageMode_f);
244         Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
245         Cmd_AddCommand ("clear", Con_Clear_f);
246         con_initialized = true;
247 }
248
249
250 /*
251 ===============
252 Con_Linefeed
253 ===============
254 */
255 void Con_Linefeed (void)
256 {
257         con_x = 0;
258         con_current++;
259         memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
260 }
261
262 /*
263 ================
264 Con_Print
265
266 Handles cursor positioning, line wrapping, etc
267 All console printing must go through this in order to be logged to disk
268 If no console is visible, the notify window will pop up.
269 ================
270 */
271 void Con_Print (const char *txt)
272 {
273         int y, c, l, mask;
274         static int cr;
275
276         con_backscroll = 0;
277
278         if (txt[0] == 1)
279         {
280                 mask = 128;             // go to colored text
281                 S_LocalSound ("misc/talk.wav");
282         // play talk wav
283                 txt++;
284         }
285         else if (txt[0] == 2)
286         {
287                 mask = 128;             // go to colored text
288                 txt++;
289         }
290         else
291                 mask = 0;
292
293
294         while ( (c = *txt) )
295         {
296         // count word length
297                 for (l=0 ; l< con_linewidth ; l++)
298                         if ( txt[l] <= ' ')
299                                 break;
300
301         // word wrap
302                 if (l != con_linewidth && (con_x + l > con_linewidth) )
303                         con_x = 0;
304
305                 txt++;
306
307                 if (cr)
308                 {
309                         con_current--;
310                         cr = false;
311                 }
312
313
314                 if (!con_x)
315                 {
316                         Con_Linefeed ();
317                 // mark time for transparent overlay
318                         if (con_current >= 0)
319                         {
320                                 if (con_notify.integer < 0)
321                                         Cvar_SetValueQuick(&con_notify, 0);
322                                 if (con_notifylines > MAX_NOTIFYLINES)
323                                         Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
324                                 if (con_notify.integer > 0)
325                                         con_times[con_current % con_notify.integer] = realtime;
326                         }
327                 }
328
329                 switch (c)
330                 {
331                 case '\n':
332                         con_x = 0;
333                         break;
334
335                 case '\r':
336                         con_x = 0;
337                         cr = 1;
338                         break;
339
340                 default:        // display character and advance
341                         y = con_current % con_totallines;
342                         con_text[y*con_linewidth+con_x] = c | mask;
343                         con_x++;
344                         if (con_x >= con_linewidth)
345                                 con_x = 0;
346                         break;
347                 }
348
349         }
350 }
351
352
353 /*
354 ================
355 Con_DebugLog
356 ================
357 */
358 void Con_DebugLog (const char *msg)
359 {
360     qfile_t* file;
361
362     file = FS_Open ("qconsole.log", "at", true);
363         if (file)
364         {
365                 FS_Printf (file, "%s", msg);
366                 FS_Close (file);
367         }
368 }
369
370
371 /*
372 ================
373 Con_Printf
374
375 Handles cursor positioning, line wrapping, etc
376 ================
377 */
378 // LordHavoc: increased from 4096 to 16384
379 #define MAXPRINTMSG     16384
380 // FIXME: make a buffer size safe vsprintf?
381 void Con_Printf (const char *fmt, ...)
382 {
383         va_list argptr;
384         char msg[MAXPRINTMSG];
385
386         va_start (argptr,fmt);
387         vsprintf (msg,fmt,argptr);
388         va_end (argptr);
389
390         // also echo to debugging console
391         Sys_Printf ("%s", msg);
392
393         // log all messages to file
394         if (con_debuglog)
395                 Con_DebugLog (msg);
396
397         if (!con_initialized)
398                 return;
399
400         if (cls.state == ca_dedicated)
401                 return;         // no graphics mode
402
403         // write it to the scrollable buffer
404         Con_Print (msg);
405 }
406
407 /*
408 ================
409 Con_DPrintf
410
411 A Con_Printf that only shows up if the "developer" cvar is set
412 ================
413 */
414 void Con_DPrintf (const char *fmt, ...)
415 {
416         va_list argptr;
417         char msg[MAXPRINTMSG];
418
419         if (!developer.integer)
420                 return;                 // don't confuse non-developers with techie stuff...
421
422         va_start (argptr,fmt);
423         vsprintf (msg,fmt,argptr);
424         va_end (argptr);
425
426         Con_Printf ("%s", msg);
427 }
428
429
430 /*
431 ==================
432 Con_SafePrintf
433
434 Okay to call even when the screen can't be updated
435 ==================
436 */
437 void Con_SafePrintf (const char *fmt, ...)
438 {
439         va_list         argptr;
440         char            msg[1024];
441
442         va_start (argptr,fmt);
443         vsprintf (msg,fmt,argptr);
444         va_end (argptr);
445
446         Con_Printf ("%s", msg);
447 }
448
449
450 /*
451 ==============================================================================
452
453 DRAWING
454
455 ==============================================================================
456 */
457
458
459 /*
460 ================
461 Con_DrawInput
462
463 The input line scrolls horizontally if typing goes beyond the right edge
464
465 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
466 ================
467 */
468 void Con_DrawInput (void)
469 {
470         char editlinecopy[256], *text;
471
472         if (!key_consoleactive)
473                 return;         // don't draw anything
474
475         text = strcpy(editlinecopy, key_lines[edit_line]);
476
477         // Advanced Console Editing by Radix radix@planetquake.com
478         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
479         // use strlen of edit_line instead of key_linepos to allow editing
480         // of early characters w/o erasing
481
482         // add the cursor frame
483         if ((int)(realtime*con_cursorspeed) & 1)                // cursor is visible
484                 text[key_linepos] = 11 + 130 * key_insert;      // either solid or triangle facing right
485
486         text[key_linepos + 1] = 0;
487
488         // prestep if horizontally scrolling
489         if (key_linepos >= con_linewidth)
490                 text += 1 + key_linepos - con_linewidth;
491
492         // draw it
493         DrawQ_String(0, con_vislines - 16, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
494
495         // remove cursor
496         key_lines[edit_line][key_linepos] = 0;
497 }
498
499
500 /*
501 ================
502 Con_DrawNotify
503
504 Draws the last few lines of output transparently over the game top
505 ================
506 */
507 void Con_DrawNotify (void)
508 {
509         int             x, v;
510         char    *text;
511         int             i;
512         float   time;
513         extern char chat_buffer[];
514         char    temptext[256];
515
516         if (con_notify.integer < 0)
517                 Cvar_SetValueQuick(&con_notify, 0);
518         if (con_notifylines > MAX_NOTIFYLINES)
519                 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
520         v = 0;
521         for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
522         {
523                 if (i < 0)
524                         continue;
525                 time = con_times[i % con_notify.integer];
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 (chat_team)
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) >= (size_t) 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 (con_notifylines < v)
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, rows, j;
582         char *text;
583
584         if (lines <= 0)
585                 return;
586
587 // draw the background
588         DrawQ_Pic(0, lines - vid.conheight, "gfx/conback", vid.conwidth, vid.conheight, 1, 1, 1, scr_conalpha.value * lines / vid.conheight, 0);
589         DrawQ_String(vid.conwidth - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
590
591 // draw the text
592         con_vislines = lines;
593
594         rows = (lines-16)>>3;           // rows of text to draw
595         y = lines - 16 - (rows<<3);     // may start slightly negative
596
597         for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
598         {
599                 j = max(i - con_backscroll, 0);
600                 text = con_text + (j % con_totallines)*con_linewidth;
601
602                 DrawQ_String(0, y, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
603         }
604
605 // draw the input prompt, user text, and cursor if desired
606         Con_DrawInput ();
607 }
608
609 /*
610         Con_DisplayList
611
612         New function for tab-completion system
613         Added by EvilTypeGuy
614         MEGA Thanks to Taniwha
615
616 */
617 void Con_DisplayList(const char **list)
618 {
619         int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
620         const char **walk = list;
621
622         while (*walk) {
623                 len = strlen(*walk);
624                 if (len > maxlen)
625                         maxlen = len;
626                 walk++;
627         }
628         maxlen += 1;
629
630         while (*list) {
631                 len = strlen(*list);
632                 if (pos + maxlen >= width) {
633                         Con_Printf("\n");
634                         pos = 0;
635                 }
636
637                 Con_Printf("%s", *list);
638                 for (i = 0; i < (maxlen - len); i++)
639                         Con_Printf(" ");
640
641                 pos += maxlen;
642                 list++;
643         }
644
645         if (pos)
646                 Con_Printf("\n\n");
647 }
648
649 /*
650         Con_CompleteCommandLine
651
652         New function for tab-completion system
653         Added by EvilTypeGuy
654         Thanks to Fett erich@heintz.com
655         Thanks to taniwha
656
657 */
658 void Con_CompleteCommandLine (void)
659 {
660         const char *cmd = "", *s;
661         const char **list[3] = {0, 0, 0};
662         int c, v, a, i, cmd_len;
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                                 const 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((void *)list[i]);
740 }
741