Added the togglemenu command for both menus and the chr builtin command (menu qc)
[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 extern void M_Menu_Main_f (void);
77
78 /*
79 ================
80 Con_ToggleConsole_f
81 ================
82 */
83 void Con_ToggleConsole_f (void)
84 {
85         // toggle the 'user wants console' bit
86         key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
87         memset (con_times, 0, sizeof(con_times));
88 }
89
90 /*
91 ================
92 Con_Clear_f
93 ================
94 */
95 void Con_Clear_f (void)
96 {
97         if (con_text)
98                 memset (con_text, ' ', CON_TEXTSIZE);
99 }
100
101
102 /*
103 ================
104 Con_ClearNotify
105 ================
106 */
107 void Con_ClearNotify (void)
108 {
109         int i;
110
111         for (i=0 ; i<MAX_NOTIFYLINES ; i++)
112                 con_times[i] = 0;
113 }
114
115
116 /*
117 ================
118 Con_MessageMode_f
119 ================
120 */
121 void Con_MessageMode_f (void)
122 {
123         key_dest = key_message;
124         chat_team = false;
125 }
126
127
128 /*
129 ================
130 Con_MessageMode2_f
131 ================
132 */
133 void Con_MessageMode2_f (void)
134 {
135         key_dest = key_message;
136         chat_team = true;
137 }
138
139
140 /*
141 ================
142 Con_CheckResize
143
144 If the line width has changed, reformat the buffer.
145 ================
146 */
147 void Con_CheckResize (void)
148 {
149         int i, j, width, oldwidth, oldtotallines, numlines, numchars;
150         char tbuf[CON_TEXTSIZE];
151
152         width = (vid.conwidth >> 3);
153
154         if (width == con_linewidth)
155                 return;
156
157         if (width < 1)                  // video hasn't been initialized yet
158         {
159                 width = 80;
160                 con_linewidth = width;
161                 con_totallines = CON_TEXTSIZE / con_linewidth;
162                 memset (con_text, ' ', CON_TEXTSIZE);
163         }
164         else
165         {
166                 oldwidth = con_linewidth;
167                 con_linewidth = width;
168                 oldtotallines = con_totallines;
169                 con_totallines = CON_TEXTSIZE / con_linewidth;
170                 numlines = oldtotallines;
171
172                 if (con_totallines < numlines)
173                         numlines = con_totallines;
174
175                 numchars = oldwidth;
176
177                 if (con_linewidth < numchars)
178                         numchars = con_linewidth;
179
180                 memcpy (tbuf, con_text, CON_TEXTSIZE);
181                 memset (con_text, ' ', CON_TEXTSIZE);
182
183                 for (i=0 ; i<numlines ; i++)
184                 {
185                         for (j=0 ; j<numchars ; j++)
186                         {
187                                 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
188                                                 tbuf[((con_current - i + oldtotallines) %
189                                                           oldtotallines) * oldwidth + j];
190                         }
191                 }
192
193                 Con_ClearNotify ();
194         }
195
196         con_backscroll = 0;
197         con_current = con_totallines - 1;
198 }
199
200
201 void Con_InitLogging (void)
202 {
203 #define MAXGAMEDIRLEN 1000
204         char temp[MAXGAMEDIRLEN+1];
205         char *t2 = "/qconsole.log";
206
207         con_debuglog = COM_CheckParm("-condebug");
208         if (con_debuglog)
209         {
210                 if (strlen (fs_gamedir) < (MAXGAMEDIRLEN - strlen (t2)))
211                 {
212                         sprintf (temp, "%s%s", fs_gamedir, t2);
213                         unlink (temp);
214                 }
215                 logfile.integer = 1;
216         }
217 }
218
219 /*
220 ================
221 Con_Init
222 ================
223 */
224 void Con_Init (void)
225 {
226         Cvar_RegisterVariable(&logfile);
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 displayed
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_notify.integer > 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_notify.integer > 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
566 /*
567 ================
568 Con_DrawConsole
569
570 Draws the console with the solid background
571 The typing input line at the bottom should only be drawn if typing is allowed
572 ================
573 */
574 extern char engineversion[40];
575 void Con_DrawConsole (int lines)
576 {
577         int i, y, rows, j;
578         char *text;
579
580         if (lines <= 0)
581                 return;
582
583 // draw the background
584         if (scr_conbrightness.value >= 0.01f)
585                 DrawQ_Pic(0, lines - vid.conheight, "gfx/conback", vid.conwidth, vid.conheight, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, scr_conalpha.value, 0);
586         else
587                 DrawQ_Fill(0, lines - vid.conheight, vid.conwidth, vid.conheight, 0, 0, 0, scr_conalpha.value, 0);
588         DrawQ_String(vid.conwidth - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
589
590 // draw the text
591         con_vislines = lines;
592
593         rows = (lines-16)>>3;           // rows of text to draw
594         y = lines - 16 - (rows<<3);     // may start slightly negative
595
596         for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
597         {
598                 j = max(i - con_backscroll, 0);
599                 text = con_text + (j % con_totallines)*con_linewidth;
600
601                 DrawQ_String(0, y, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
602         }
603
604 // draw the input prompt, user text, and cursor if desired
605         Con_DrawInput ();
606 }
607
608 /*
609         Con_DisplayList
610
611         New function for tab-completion system
612         Added by EvilTypeGuy
613         MEGA Thanks to Taniwha
614
615 */
616 void Con_DisplayList(const char **list)
617 {
618         int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
619         const char **walk = list;
620
621         while (*walk) {
622                 len = strlen(*walk);
623                 if (len > maxlen)
624                         maxlen = len;
625                 walk++;
626         }
627         maxlen += 1;
628
629         while (*list) {
630                 len = strlen(*list);
631                 if (pos + maxlen >= width) {
632                         Con_Printf("\n");
633                         pos = 0;
634                 }
635
636                 Con_Printf("%s", *list);
637                 for (i = 0; i < (maxlen - len); i++)
638                         Con_Printf(" ");
639
640                 pos += maxlen;
641                 list++;
642         }
643
644         if (pos)
645                 Con_Printf("\n\n");
646 }
647
648 /*
649         Con_CompleteCommandLine
650
651         New function for tab-completion system
652         Added by EvilTypeGuy
653         Thanks to Fett erich@heintz.com
654         Thanks to taniwha
655
656 */
657 void Con_CompleteCommandLine (void)
658 {
659         const char *cmd = "", *s;
660         const char **list[3] = {0, 0, 0};
661         int c, v, a, i, cmd_len;
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                                 const 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((void *)list[i]);
739 }
740