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