patch from transfusion team to change transfusion hud layout a bit (moves minideathma...
[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 #ifndef WIN32
23 # include <unistd.h>
24 #endif
25 #include "quakedef.h"
26
27 int con_linewidth;
28
29 float con_cursorspeed = 4;
30
31 #define         CON_TEXTSIZE    131072
32
33 // total lines in console scrollback
34 int con_totallines;
35 // lines up from bottom to display
36 int con_backscroll;
37 // where next message will be printed
38 int con_current;
39 // offset in current line for next print
40 int con_x;
41 char *con_text = 0;
42
43 //seconds
44 cvar_t con_notifytime = {CVAR_SAVE, "con_notifytime","3"};
45 cvar_t con_notify = {CVAR_SAVE, "con_notify","4"};
46
47 #define MAX_NOTIFYLINES 32
48 // cl.time time the line was generated for transparent notify lines
49 float con_times[MAX_NOTIFYLINES];
50
51 int con_vislines;
52
53 #define MAXCMDLINE      256
54 extern char key_lines[32][MAXCMDLINE];
55 extern int edit_line;
56 extern int key_linepos;
57 extern int key_insert;
58
59
60 qboolean con_initialized;
61
62 mempool_t *console_mempool;
63
64
65 /*
66 ==============================================================================
67
68 LOGGING
69
70 ==============================================================================
71 */
72
73 cvar_t log_file = {0, "log_file",""};
74 cvar_t log_sync = {0, "log_sync","0"};
75 qfile_t* logfile = NULL;
76
77 qbyte* logqueue = NULL;
78 size_t logq_ind = 0;
79 size_t logq_size = 0;
80
81 /*
82 ====================
83 Log_Init
84 ====================
85 */
86 void Log_Init (void)
87 {
88         Cvar_RegisterVariable (&log_file);
89         Cvar_RegisterVariable (&log_sync);
90
91         // support for the classic Quake option
92         if (COM_CheckParm ("-condebug") != 0)
93         {
94                 Cvar_SetQuick (&log_file, "qconsole.log");
95                 Cvar_SetValueQuick (&log_sync, 1);
96         }
97
98         // Allocate a log queue
99         logq_size = 256;
100         logqueue = Mem_Alloc (tempmempool, logq_size);
101         logq_ind = 0;
102 }
103
104
105 /*
106 ====================
107 Log_Start
108 ====================
109 */
110 void Log_Start (void)
111 {
112         if (log_file.string[0] != '\0')
113                 logfile = FS_Open (log_file.string, "wt", false);
114
115         // Dump the contents of the log queue into the log file and free it
116         if (logqueue != NULL)
117         {
118                 if (logfile != NULL && logq_ind != 0)
119                         FS_Write (logfile, logqueue, logq_ind);
120                 Mem_Free (logqueue);
121                 logqueue = NULL;
122                 logq_ind = 0;
123                 logq_size = 0;
124         }
125 }
126
127
128 /*
129 ================
130 Log_ConPrint
131 ================
132 */
133 void Log_ConPrint (const char *msg)
134 {
135         // Easy case: a log has been started
136         if (logfile != NULL)
137         {
138                 FS_Print (logfile, msg);
139                 if (log_sync.integer)
140                         FS_Flush (logfile);
141                 return;
142         }
143
144         // Until the host is completely initialized, we maintain a log queue
145         // to store the messages, since the log can't be started before
146         if (logqueue != NULL)
147         {
148                 size_t remain = logq_size - logq_ind;
149                 size_t len = strlen (msg);
150
151                 // If we need to enlarge the log queue
152                 if (len > remain)
153                 {
154                         unsigned int factor = ((logq_ind + len) / logq_size) + 1;
155                         qbyte* newqueue;
156
157                         logq_size *= factor;
158                         newqueue = Mem_Alloc (tempmempool, logq_size);
159                         memcpy (newqueue, logqueue, logq_ind);
160                         Mem_Free (logqueue);
161                         logqueue = newqueue;
162                         remain = logq_size - logq_ind;
163                 }
164                 memcpy (&logqueue[logq_ind], msg, len);
165                 logq_ind += len;
166         }
167 }
168
169
170 /*
171 ================
172 Log_Print
173 ================
174 */
175 void Log_Print (const char *logfilename, const char *msg)
176 {
177         qfile_t *file;
178         file = FS_Open(logfilename, "at", true);
179         if (file)
180         {
181                 FS_Print(file, msg);
182                 FS_Close(file);
183         }
184 }
185
186 /*
187 ================
188 Log_Printf
189 ================
190 */
191 void Log_Printf (const char *logfilename, const char *fmt, ...)
192 {
193         qfile_t *file;
194
195         file = FS_Open (logfilename, "at", true);
196         if (file != NULL)
197         {
198                 va_list argptr;
199
200                 va_start (argptr, fmt);
201                 FS_VPrintf (file, fmt, argptr);
202                 va_end (argptr);
203
204                 FS_Close (file);
205         }
206 }
207
208
209 /*
210 ==============================================================================
211
212 CONSOLE
213
214 ==============================================================================
215 */
216
217 /*
218 ================
219 Con_ToggleConsole_f
220 ================
221 */
222 void Con_ToggleConsole_f (void)
223 {
224         // toggle the 'user wants console' bit
225         key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
226         memset (con_times, 0, sizeof(con_times));
227 }
228
229 /*
230 ================
231 Con_Clear_f
232 ================
233 */
234 void Con_Clear_f (void)
235 {
236         if (con_text)
237                 memset (con_text, ' ', CON_TEXTSIZE);
238 }
239
240
241 /*
242 ================
243 Con_ClearNotify
244 ================
245 */
246 void Con_ClearNotify (void)
247 {
248         int i;
249
250         for (i=0 ; i<MAX_NOTIFYLINES ; i++)
251                 con_times[i] = 0;
252 }
253
254
255 /*
256 ================
257 Con_MessageMode_f
258 ================
259 */
260 void Con_MessageMode_f (void)
261 {
262         key_dest = key_message;
263         chat_team = false;
264 }
265
266
267 /*
268 ================
269 Con_MessageMode2_f
270 ================
271 */
272 void Con_MessageMode2_f (void)
273 {
274         key_dest = key_message;
275         chat_team = true;
276 }
277
278
279 /*
280 ================
281 Con_CheckResize
282
283 If the line width has changed, reformat the buffer.
284 ================
285 */
286 void Con_CheckResize (void)
287 {
288         int i, j, width, oldwidth, oldtotallines, numlines, numchars;
289         char tbuf[CON_TEXTSIZE];
290
291         width = (vid.conwidth >> 3);
292
293         if (width == con_linewidth)
294                 return;
295
296         if (width < 1)                  // video hasn't been initialized yet
297         {
298                 width = 80;
299                 con_linewidth = width;
300                 con_totallines = CON_TEXTSIZE / con_linewidth;
301                 memset (con_text, ' ', CON_TEXTSIZE);
302         }
303         else
304         {
305                 oldwidth = con_linewidth;
306                 con_linewidth = width;
307                 oldtotallines = con_totallines;
308                 con_totallines = CON_TEXTSIZE / con_linewidth;
309                 numlines = oldtotallines;
310
311                 if (con_totallines < numlines)
312                         numlines = con_totallines;
313
314                 numchars = oldwidth;
315
316                 if (con_linewidth < numchars)
317                         numchars = con_linewidth;
318
319                 memcpy (tbuf, con_text, CON_TEXTSIZE);
320                 memset (con_text, ' ', CON_TEXTSIZE);
321
322                 for (i=0 ; i<numlines ; i++)
323                 {
324                         for (j=0 ; j<numchars ; j++)
325                         {
326                                 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
327                                                 tbuf[((con_current - i + oldtotallines) %
328                                                           oldtotallines) * oldwidth + j];
329                         }
330                 }
331
332                 Con_ClearNotify ();
333         }
334
335         con_backscroll = 0;
336         con_current = con_totallines - 1;
337 }
338
339 /*
340 ================
341 Con_Init
342 ================
343 */
344 void Con_Init (void)
345 {
346         console_mempool = Mem_AllocPool("console");
347         con_text = Mem_Alloc(console_mempool, CON_TEXTSIZE);
348         memset (con_text, ' ', CON_TEXTSIZE);
349         con_linewidth = -1;
350         Con_CheckResize ();
351
352         Con_Print("Console initialized.\n");
353
354         // register our cvars
355         Cvar_RegisterVariable (&con_notifytime);
356         Cvar_RegisterVariable (&con_notify);
357
358         // register our commands
359         Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
360         Cmd_AddCommand ("messagemode", Con_MessageMode_f);
361         Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
362         Cmd_AddCommand ("clear", Con_Clear_f);
363         con_initialized = true;
364 }
365
366
367 /*
368 ===============
369 Con_Linefeed
370 ===============
371 */
372 void Con_Linefeed (void)
373 {
374         con_x = 0;
375         con_current++;
376         memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
377 }
378
379 /*
380 ================
381 Con_PrintToHistory
382
383 Handles cursor positioning, line wrapping, etc
384 All console printing must go through this in order to be displayed
385 If no console is visible, the notify window will pop up.
386 ================
387 */
388 void Con_PrintToHistory(const char *txt)
389 {
390         int y, c, l, mask;
391         static int cr;
392
393         con_backscroll = 0;
394
395         if (txt[0] == 1)
396         {
397                 mask = 128;             // go to colored text
398                 S_LocalSound ("misc/talk.wav");
399         // play talk wav
400                 txt++;
401         }
402         else if (txt[0] == 2)
403         {
404                 mask = 128;             // go to colored text
405                 txt++;
406         }
407         else
408                 mask = 0;
409
410
411         while ( (c = *txt) )
412         {
413         // count word length
414                 for (l=0 ; l< con_linewidth ; l++)
415                         if ( txt[l] <= ' ')
416                                 break;
417
418         // word wrap
419                 if (l != con_linewidth && (con_x + l > con_linewidth) )
420                         con_x = 0;
421
422                 txt++;
423
424                 if (cr)
425                 {
426                         con_current--;
427                         cr = false;
428                 }
429
430
431                 if (!con_x)
432                 {
433                         Con_Linefeed ();
434                 // mark time for transparent overlay
435                         if (con_current >= 0)
436                         {
437                                 if (con_notify.integer < 0)
438                                         Cvar_SetValueQuick(&con_notify, 0);
439                                 if (con_notify.integer > MAX_NOTIFYLINES)
440                                         Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
441                                 if (con_notify.integer > 0)
442                                         con_times[con_current % con_notify.integer] = cl.time;
443                         }
444                 }
445
446                 switch (c)
447                 {
448                 case '\n':
449                         con_x = 0;
450                         break;
451
452                 case '\r':
453                         con_x = 0;
454                         cr = 1;
455                         break;
456
457                 default:        // display character and advance
458                         y = con_current % con_totallines;
459                         con_text[y*con_linewidth+con_x] = c | mask;
460                         con_x++;
461                         if (con_x >= con_linewidth)
462                                 con_x = 0;
463                         break;
464                 }
465
466         }
467 }
468
469 /*
470 ================
471 Con_Print
472
473 Prints to all appropriate console targets
474 ================
475 */
476 void Con_Print(const char *msg)
477 {
478         // also echo to debugging console
479         Sys_Print(msg);
480
481         // log all messages to file
482         Log_ConPrint (msg);
483
484         if (!con_initialized)
485                 return;
486
487         if (cls.state == ca_dedicated)
488                 return;         // no graphics mode
489
490         // write it to the scrollable buffer
491         Con_PrintToHistory(msg);
492 }
493
494
495 // LordHavoc: increased from 4096 to 16384
496 #define MAXPRINTMSG     16384
497
498 /*
499 ================
500 Con_Printf
501
502 Prints to all appropriate console targets
503 ================
504 */
505 void Con_Printf(const char *fmt, ...)
506 {
507         va_list argptr;
508         char msg[MAXPRINTMSG];
509
510         va_start(argptr,fmt);
511         vsprintf(msg,fmt,argptr);
512         va_end(argptr);
513
514         Con_Print(msg);
515 }
516
517 /*
518 ================
519 Con_DPrint
520
521 A Con_Print that only shows up if the "developer" cvar is set
522 ================
523 */
524 void Con_DPrint(const char *msg)
525 {
526         if (!developer.integer)
527                 return;                 // don't confuse non-developers with techie stuff...
528         Con_Print(msg);
529 }
530
531 /*
532 ================
533 Con_DPrintf
534
535 A Con_Printf that only shows up if the "developer" cvar is set
536 ================
537 */
538 void Con_DPrintf(const char *fmt, ...)
539 {
540         va_list argptr;
541         char msg[MAXPRINTMSG];
542
543         if (!developer.integer)
544                 return;                 // don't confuse non-developers with techie stuff...
545
546         va_start(argptr,fmt);
547         vsprintf(msg,fmt,argptr);
548         va_end(argptr);
549
550         Con_Print(msg);
551 }
552
553
554 /*
555 ================
556 Con_SafePrint
557
558 Okay to call even when the screen can't be updated
559 ==================
560 */
561 void Con_SafePrint(const char *msg)
562 {
563         Con_Print(msg);
564 }
565
566 /*
567 ==================
568 Con_SafePrintf
569
570 Okay to call even when the screen can't be updated
571 ==================
572 */
573 void Con_SafePrintf(const char *fmt, ...)
574 {
575         va_list argptr;
576         char msg[MAXPRINTMSG];
577
578         va_start(argptr,fmt);
579         vsprintf(msg,fmt,argptr);
580         va_end(argptr);
581
582         Con_Print(msg);
583 }
584
585
586 /*
587 ==============================================================================
588
589 DRAWING
590
591 ==============================================================================
592 */
593
594
595 /*
596 ================
597 Con_DrawInput
598
599 The input line scrolls horizontally if typing goes beyond the right edge
600
601 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
602 ================
603 */
604 void Con_DrawInput (void)
605 {
606         char editlinecopy[257], *text;
607
608         if (!key_consoleactive)
609                 return;         // don't draw anything
610
611         text = strcpy(editlinecopy, key_lines[edit_line]);
612
613         // Advanced Console Editing by Radix radix@planetquake.com
614         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
615         // use strlen of edit_line instead of key_linepos to allow editing
616         // of early characters w/o erasing
617
618         // add the cursor frame
619         if ((int)(realtime*con_cursorspeed) & 1)                // cursor is visible
620                 text[key_linepos] = 11 + 130 * key_insert;      // either solid or triangle facing right
621
622         text[key_linepos + 1] = 0;
623
624         // prestep if horizontally scrolling
625         if (key_linepos >= con_linewidth)
626                 text += 1 + key_linepos - con_linewidth;
627
628         // draw it
629         DrawQ_String(0, con_vislines - 16, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
630
631         // remove cursor
632         key_lines[edit_line][key_linepos] = 0;
633 }
634
635
636 /*
637 ================
638 Con_DrawNotify
639
640 Draws the last few lines of output transparently over the game top
641 ================
642 */
643 void Con_DrawNotify (void)
644 {
645         int             x, v;
646         char    *text;
647         int             i;
648         float   time;
649         extern char chat_buffer[];
650         char    temptext[256];
651
652         if (con_notify.integer < 0)
653                 Cvar_SetValueQuick(&con_notify, 0);
654         if (con_notify.integer > MAX_NOTIFYLINES)
655                 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
656         if (gamemode == GAME_TRANSFUSION)
657                 v = 8;
658         else
659                 v = 0;
660         for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
661         {
662                 if (i < 0)
663                         continue;
664                 time = con_times[i % con_notify.integer];
665                 if (time == 0)
666                         continue;
667                 time = cl.time - time;
668                 if (time > con_notifytime.value)
669                         continue;
670                 text = con_text + (i % con_totallines)*con_linewidth;
671
672                 clearnotify = 0;
673
674                 DrawQ_String(0, v, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
675
676                 v += 8;
677         }
678
679
680         if (key_dest == key_message)
681         {
682                 clearnotify = 0;
683
684                 x = 0;
685
686                 // LordHavoc: speedup, and other improvements
687                 if (chat_team)
688                         sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
689                 else
690                         sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
691                 while (strlen(temptext) >= (size_t) con_linewidth)
692                 {
693                         DrawQ_String (0, v, temptext, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
694                         strcpy(temptext, &temptext[con_linewidth]);
695                         v += 8;
696                 }
697                 if (strlen(temptext) > 0)
698                 {
699                         DrawQ_String (0, v, temptext, 0, 8, 8, 1, 1, 1, 1, 0);
700                         v += 8;
701                 }
702         }
703 }
704
705 /*
706 ================
707 Con_DrawConsole
708
709 Draws the console with the solid background
710 The typing input line at the bottom should only be drawn if typing is allowed
711 ================
712 */
713 extern char engineversion[40];
714 void Con_DrawConsole (int lines)
715 {
716         int i, y, rows, j;
717         char *text;
718
719         if (lines <= 0)
720                 return;
721
722 // draw the background
723         if (scr_conbrightness.value >= 0.01f)
724                 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);
725         else
726                 DrawQ_Fill(0, lines - vid.conheight, vid.conwidth, vid.conheight, 0, 0, 0, scr_conalpha.value, 0);
727         DrawQ_String(vid.conwidth - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
728
729 // draw the text
730         con_vislines = lines;
731
732         rows = (lines-16)>>3;           // rows of text to draw
733         y = lines - 16 - (rows<<3);     // may start slightly negative
734
735         for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
736         {
737                 j = max(i - con_backscroll, 0);
738                 text = con_text + (j % con_totallines)*con_linewidth;
739
740                 DrawQ_String(0, y, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
741         }
742
743 // draw the input prompt, user text, and cursor if desired
744         Con_DrawInput ();
745 }
746
747 /*
748         Con_DisplayList
749
750         New function for tab-completion system
751         Added by EvilTypeGuy
752         MEGA Thanks to Taniwha
753
754 */
755 void Con_DisplayList(const char **list)
756 {
757         int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
758         const char **walk = list;
759
760         while (*walk) {
761                 len = strlen(*walk);
762                 if (len > maxlen)
763                         maxlen = len;
764                 walk++;
765         }
766         maxlen += 1;
767
768         while (*list) {
769                 len = strlen(*list);
770                 if (pos + maxlen >= width) {
771                         Con_Print("\n");
772                         pos = 0;
773                 }
774
775                 Con_Print(*list);
776                 for (i = 0; i < (maxlen - len); i++)
777                         Con_Print(" ");
778
779                 pos += maxlen;
780                 list++;
781         }
782
783         if (pos)
784                 Con_Print("\n\n");
785 }
786
787 /*
788         Con_CompleteCommandLine
789
790         New function for tab-completion system
791         Added by EvilTypeGuy
792         Thanks to Fett erich@heintz.com
793         Thanks to taniwha
794
795 */
796 void Con_CompleteCommandLine (void)
797 {
798         const char *cmd = "", *s;
799         const char **list[3] = {0, 0, 0};
800         int c, v, a, i, cmd_len;
801
802         s = key_lines[edit_line] + 1;
803         // Count number of possible matches
804         c = Cmd_CompleteCountPossible(s);
805         v = Cvar_CompleteCountPossible(s);
806         a = Cmd_CompleteAliasCountPossible(s);
807
808         if (!(c + v + a))       // No possible matches
809                 return;
810
811         if (c + v + a == 1) {
812                 if (c)
813                         list[0] = Cmd_CompleteBuildList(s);
814                 else if (v)
815                         list[0] = Cvar_CompleteBuildList(s);
816                 else
817                         list[0] = Cmd_CompleteAliasBuildList(s);
818                 cmd = *list[0];
819                 cmd_len = strlen (cmd);
820         } else {
821                 if (c)
822                         cmd = *(list[0] = Cmd_CompleteBuildList(s));
823                 if (v)
824                         cmd = *(list[1] = Cvar_CompleteBuildList(s));
825                 if (a)
826                         cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
827
828                 cmd_len = strlen (s);
829                 do {
830                         for (i = 0; i < 3; i++) {
831                                 char ch = cmd[cmd_len];
832                                 const char **l = list[i];
833                                 if (l) {
834                                         while (*l && (*l)[cmd_len] == ch)
835                                                 l++;
836                                         if (*l)
837                                                 break;
838                                 }
839                         }
840                         if (i == 3)
841                                 cmd_len++;
842                 } while (i == 3);
843                 // 'quakebar'
844                 Con_Print("\n\35");
845                 for (i = 0; i < con_linewidth - 4; i++)
846                         Con_Print("\36");
847                 Con_Print("\37\n");
848
849                 // Print Possible Commands
850                 if (c) {
851                         Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
852                         Con_DisplayList(list[0]);
853                 }
854
855                 if (v) {
856                         Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
857                         Con_DisplayList(list[1]);
858                 }
859
860                 if (a) {
861                         Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
862                         Con_DisplayList(list[2]);
863                 }
864         }
865
866         if (cmd) {
867                 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
868                 key_linepos = cmd_len + 1;
869                 if (c + v + a == 1) {
870                         key_lines[edit_line][key_linepos] = ' ';
871                         key_linepos++;
872                 }
873                 key_lines[edit_line][key_linepos] = 0;
874         }
875         for (i = 0; i < 3; i++)
876                 if (list[i])
877                         Mem_Free((void *)list[i]);
878 }
879