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