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