]> icculus.org git repositories - divverent/darkplaces.git/blob - console.c
support for top to bottom TGA, thanks Elric.
[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    16384
41
42 qboolean        con_forcedup;           // because no entities to refresh
43
44 int                     con_totallines;         // total lines in console scrollback
45 int                     con_backscroll;         // lines up from bottom to display
46 int                     con_current;            // where next message will be printed
47 int                     con_x;                          // offset in current line for next print
48 char            *con_text = 0;
49
50 cvar_t          con_notifytime = {CVAR_SAVE, "con_notifytime","3"};     //seconds
51 cvar_t          logfile = {0, "logfile","0"};
52
53 #define NUM_CON_TIMES 4
54 float           con_times[NUM_CON_TIMES];       // realtime time the line was generated
55                                                 // for transparent notify lines
56
57 int                     con_vislines;
58
59 qboolean        con_debuglog;
60
61 #define MAXCMDLINE      256
62 extern  char    key_lines[32][MAXCMDLINE];
63 extern  int             edit_line;
64 extern  int             key_linepos;
65 extern  int             key_insert;
66
67
68 qboolean        con_initialized;
69
70 mempool_t       *console_mempool;
71
72 int             con_notifylines;                // scan lines to clear for notify lines
73
74 extern void M_Menu_Main_f (void);
75
76 /*
77 ================
78 Con_ToggleConsole_f
79 ================
80 */
81 void Con_ToggleConsole_f (void)
82 {
83         if (key_dest == key_console)
84         {
85                 if (cls.state == ca_connected)
86                         key_dest = key_game;
87                 else
88                         M_Menu_Main_f ();
89         }
90         else
91                 key_dest = key_console;
92
93         memset (con_times, 0, sizeof(con_times));
94 }
95
96 /*
97 ================
98 Con_Clear_f
99 ================
100 */
101 void Con_Clear_f (void)
102 {
103         if (con_text)
104                 memset (con_text, ' ', CON_TEXTSIZE);
105 }
106
107
108 /*
109 ================
110 Con_ClearNotify
111 ================
112 */
113 void Con_ClearNotify (void)
114 {
115         int             i;
116
117         for (i=0 ; i<NUM_CON_TIMES ; i++)
118                 con_times[i] = 0;
119 }
120
121
122 /*
123 ================
124 Con_MessageMode_f
125 ================
126 */
127 extern qboolean team_message;
128
129 void Con_MessageMode_f (void)
130 {
131         key_dest = key_message;
132         team_message = false;
133 }
134
135
136 /*
137 ================
138 Con_MessageMode2_f
139 ================
140 */
141 void Con_MessageMode2_f (void)
142 {
143         key_dest = key_message;
144         team_message = true;
145 }
146
147
148 /*
149 ================
150 Con_CheckResize
151
152 If the line width has changed, reformat the buffer.
153 ================
154 */
155 void Con_CheckResize (void)
156 {
157         int             i, j, width, oldwidth, oldtotallines, numlines, numchars;
158         char    tbuf[CON_TEXTSIZE];
159
160         width = (vid.conwidth >> 3) - 2;
161
162         if (width == con_linewidth)
163                 return;
164
165         if (width < 1)                  // video hasn't been initialized yet
166         {
167                 width = 78; // LordHavoc: changed from 38 to 78 (320 -> 640 conversion)
168                 con_linewidth = width;
169                 con_totallines = CON_TEXTSIZE / con_linewidth;
170                 memset (con_text, ' ', CON_TEXTSIZE);
171         }
172         else
173         {
174                 oldwidth = con_linewidth;
175                 con_linewidth = width;
176                 oldtotallines = con_totallines;
177                 con_totallines = CON_TEXTSIZE / con_linewidth;
178                 numlines = oldtotallines;
179
180                 if (con_totallines < numlines)
181                         numlines = con_totallines;
182
183                 numchars = oldwidth;
184
185                 if (con_linewidth < numchars)
186                         numchars = con_linewidth;
187
188                 memcpy (tbuf, con_text, CON_TEXTSIZE);
189                 memset (con_text, ' ', CON_TEXTSIZE);
190
191                 for (i=0 ; i<numlines ; i++)
192                 {
193                         for (j=0 ; j<numchars ; j++)
194                         {
195                                 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
196                                                 tbuf[((con_current - i + oldtotallines) %
197                                                           oldtotallines) * oldwidth + j];
198                         }
199                 }
200
201                 Con_ClearNotify ();
202         }
203
204         con_backscroll = 0;
205         con_current = con_totallines - 1;
206 }
207
208
209 /*
210 ================
211 Con_Init
212 ================
213 */
214 void Con_Init (void)
215 {
216 #define MAXGAMEDIRLEN   1000
217         char    temp[MAXGAMEDIRLEN+1];
218         char    *t2 = "/qconsole.log";
219
220         Cvar_RegisterVariable(&logfile);
221         con_debuglog = COM_CheckParm("-condebug");
222
223         if (con_debuglog)
224         {
225                 if (strlen (com_gamedir) < (MAXGAMEDIRLEN - strlen (t2)))
226                 {
227                         sprintf (temp, "%s%s", com_gamedir, t2);
228                         unlink (temp);
229                 }
230                 logfile.integer = 1;
231         }
232
233         console_mempool = Mem_AllocPool("console");
234         con_text = Mem_Alloc(console_mempool, CON_TEXTSIZE);
235         memset (con_text, ' ', CON_TEXTSIZE);
236         con_linewidth = -1;
237         Con_CheckResize ();
238
239         Con_Printf ("Console initialized.\n");
240
241 //
242 // register our commands
243 //
244         Cvar_RegisterVariable (&con_notifytime);
245
246         Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
247         Cmd_AddCommand ("messagemode", Con_MessageMode_f);
248         Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
249         Cmd_AddCommand ("clear", Con_Clear_f);
250         con_initialized = true;
251 }
252
253
254 /*
255 ===============
256 Con_Linefeed
257 ===============
258 */
259 void Con_Linefeed (void)
260 {
261         con_x = 0;
262         con_current++;
263         memset (&con_text[(con_current%con_totallines)*con_linewidth]
264         , ' ', con_linewidth);
265 }
266
267 /*
268 ================
269 Con_Print
270
271 Handles cursor positioning, line wrapping, etc
272 All console printing must go through this in order to be logged to disk
273 If no console is visible, the notify window will pop up.
274 ================
275 */
276 void Con_Print (char *txt)
277 {
278         int             y;
279         int             c, l;
280         static int      cr;
281         int             mask;
282
283         con_backscroll = 0;
284
285         if (txt[0] == 1)
286         {
287                 mask = 128;             // go to colored text
288                 S_LocalSound ("misc/talk.wav");
289         // play talk wav
290                 txt++;
291         }
292         else if (txt[0] == 2)
293         {
294                 mask = 128;             // go to colored text
295                 txt++;
296         }
297         else
298                 mask = 0;
299
300
301         while ( (c = *txt) )
302         {
303         // count word length
304                 for (l=0 ; l< con_linewidth ; l++)
305                         if ( txt[l] <= ' ')
306                                 break;
307
308         // word wrap
309                 if (l != con_linewidth && (con_x + l > con_linewidth) )
310                         con_x = 0;
311
312                 txt++;
313
314                 if (cr)
315                 {
316                         con_current--;
317                         cr = false;
318                 }
319
320
321                 if (!con_x)
322                 {
323                         Con_Linefeed ();
324                 // mark time for transparent overlay
325                         if (con_current >= 0)
326                                 con_times[con_current % NUM_CON_TIMES] = realtime;
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(char *file, char *fmt, ...)
359 {
360     va_list argptr;
361     static char data[1024];
362     int fd;
363
364     va_start(argptr, fmt);
365     vsprintf(data, fmt, argptr);
366     va_end(argptr);
367     fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666);
368     write(fd, data, strlen(data));
369     close(fd);
370 }
371
372
373 /*
374 ================
375 Con_Printf
376
377 Handles cursor positioning, line wrapping, etc
378 ================
379 */
380 // LordHavoc: increased from 4096 to 16384
381 #define MAXPRINTMSG     16384
382 // FIXME: make a buffer size safe vsprintf?
383 void Con_Printf (char *fmt, ...)
384 {
385         va_list         argptr;
386         char            msg[MAXPRINTMSG];
387
388         va_start (argptr,fmt);
389         vsprintf (msg,fmt,argptr);
390         va_end (argptr);
391
392 // also echo to debugging console
393         Sys_Printf ("%s", msg);
394
395 // log all messages to file
396         if (con_debuglog)
397         {
398                 // can't use va() here because it might overwrite other important things
399                 char logname[MAX_OSPATH];
400                 sprintf(logname, "%s/qconsole.log", com_gamedir);
401                 Con_DebugLog(logname, "%s", msg);
402         }
403
404         if (!con_initialized)
405                 return;
406
407         if (cls.state == ca_dedicated)
408                 return;         // no graphics mode
409
410 // write it to the scrollable buffer
411         Con_Print (msg);
412 }
413
414 /*
415 ================
416 Con_DPrintf
417
418 A Con_Printf that only shows up if the "developer" cvar is set
419 ================
420 */
421 void Con_DPrintf (char *fmt, ...)
422 {
423         va_list         argptr;
424         char            msg[MAXPRINTMSG];
425
426         if (!developer.integer)
427                 return;                 // don't confuse non-developers with techie stuff...
428
429         va_start (argptr,fmt);
430         vsprintf (msg,fmt,argptr);
431         va_end (argptr);
432
433         Con_Printf ("%s", msg);
434 }
435
436
437 /*
438 ==================
439 Con_SafePrintf
440
441 Okay to call even when the screen can't be updated
442 ==================
443 */
444 void Con_SafePrintf (char *fmt, ...)
445 {
446         va_list         argptr;
447         char            msg[1024];
448
449         va_start (argptr,fmt);
450         vsprintf (msg,fmt,argptr);
451         va_end (argptr);
452
453         Con_Printf ("%s", msg);
454 }
455
456
457 /*
458 ==============================================================================
459
460 DRAWING
461
462 ==============================================================================
463 */
464
465
466 /*
467 ================
468 Con_DrawInput
469
470 The input line scrolls horizontally if typing goes beyond the right edge
471
472 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
473 ================
474 */
475 void Con_DrawInput (void)
476 {
477         char editlinecopy[256], *text;
478
479         if (key_dest != key_console && !con_forcedup)
480                 return;         // don't draw anything
481
482         text = strcpy(editlinecopy, key_lines[edit_line]);
483
484         // Advanced Console Editing by Radix radix@planetquake.com
485         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
486         // use strlen of edit_line instead of key_linepos to allow editing
487         // of early characters w/o erasing
488
489         // add the cursor frame
490         if ((int)(realtime*con_cursorspeed) & 1)                // cursor is visible
491                 text[key_linepos] = 11 + 130 * key_insert;      // either solid or triangle facing right
492
493         text[key_linepos + 1] = 0;
494
495         // prestep if horizontally scrolling
496         if (key_linepos >= con_linewidth)
497                 text += 1 + key_linepos - con_linewidth;
498
499         // draw it
500         DrawQ_String(8, con_vislines - 16, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
501
502         // remove cursor
503         key_lines[edit_line][key_linepos] = 0;
504 }
505
506
507 /*
508 ================
509 Con_DrawNotify
510
511 Draws the last few lines of output transparently over the game top
512 ================
513 */
514 void Con_DrawNotify (void)
515 {
516         int             x, v;
517         char    *text;
518         int             i;
519         float   time;
520         extern char chat_buffer[];
521         char    temptext[256];
522
523         v = 0;
524         for (i= con_current-NUM_CON_TIMES+1 ; i<=con_current ; i++)
525         {
526                 if (i < 0)
527                         continue;
528                 time = con_times[i % NUM_CON_TIMES];
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(8, 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 (team_message)
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) >= con_linewidth)
556                 {
557                         DrawQ_String (8, 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 (8, v, temptext, 0, 8, 8, 1, 1, 1, 1, 0);
564                         v += 8;
565                 }
566         }
567
568         if (v > con_notifylines)
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 cvar_t scr_conalpha;
581 extern char engineversion[40];
582 void Con_DrawConsole (int lines)
583 {
584         int                             i, y;
585         int                             rows;
586         char                    *text;
587         int                             j;
588
589         if (lines <= 0)
590                 return;
591
592 // draw the background
593         DrawQ_Pic(0, lines - vid.conheight, "gfx/conback", vid.conwidth, vid.conheight, 1, 1, 1, scr_conalpha.value * lines / vid.conheight, 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(8, 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
623 Con_DisplayList(char **list)
624 {
625         int     i = 0;
626         int     pos = 0;
627         int     len = 0;
628         int     maxlen = 0;
629         int     width = (con_linewidth - 4);
630         char    **walk = list;
631
632         while (*walk) {
633                 len = strlen(*walk);
634                 if (len > maxlen)
635                         maxlen = len;
636                 walk++;
637         }
638         maxlen += 1;
639
640         while (*list) {
641                 len = strlen(*list);
642                 if (pos + maxlen >= width) {
643                         Con_Printf("\n");
644                         pos = 0;
645                 }
646
647                 Con_Printf("%s", *list);
648                 for (i = 0; i < (maxlen - len); i++)
649                         Con_Printf(" ");
650
651                 pos += maxlen;
652                 list++;
653         }
654
655         if (pos)
656                 Con_Printf("\n\n");
657 }
658
659 /*
660         Con_CompleteCommandLine
661
662         New function for tab-completion system
663         Added by EvilTypeGuy
664         Thanks to Fett erich@heintz.com
665         Thanks to taniwha
666
667 */
668 void
669 Con_CompleteCommandLine (void)
670 {
671         char    *cmd = "";
672         char    *s;
673         int             c, v, a, i;
674         int             cmd_len;
675         char    **list[3] = {0, 0, 0};
676
677         s = key_lines[edit_line] + 1;
678         // Count number of possible matches
679         c = Cmd_CompleteCountPossible(s);
680         v = Cvar_CompleteCountPossible(s);
681         a = Cmd_CompleteAliasCountPossible(s);
682
683         if (!(c + v + a))       // No possible matches
684                 return;
685
686         if (c + v + a == 1) {
687                 if (c)
688                         list[0] = Cmd_CompleteBuildList(s);
689                 else if (v)
690                         list[0] = Cvar_CompleteBuildList(s);
691                 else
692                         list[0] = Cmd_CompleteAliasBuildList(s);
693                 cmd = *list[0];
694                 cmd_len = strlen (cmd);
695         } else {
696                 if (c)
697                         cmd = *(list[0] = Cmd_CompleteBuildList(s));
698                 if (v)
699                         cmd = *(list[1] = Cvar_CompleteBuildList(s));
700                 if (a)
701                         cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
702
703                 cmd_len = strlen (s);
704                 do {
705                         for (i = 0; i < 3; i++) {
706                                 char ch = cmd[cmd_len];
707                                 char **l = list[i];
708                                 if (l) {
709                                         while (*l && (*l)[cmd_len] == ch)
710                                                 l++;
711                                         if (*l)
712                                                 break;
713                                 }
714                         }
715                         if (i == 3)
716                                 cmd_len++;
717                 } while (i == 3);
718                 // 'quakebar'
719                 Con_Printf("\n\35");
720                 for (i = 0; i < con_linewidth - 4; i++)
721                         Con_Printf("\36");
722                 Con_Printf("\37\n");
723
724                 // Print Possible Commands
725                 if (c) {
726                         Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
727                         Con_DisplayList(list[0]);
728                 }
729
730                 if (v) {
731                         Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
732                         Con_DisplayList(list[1]);
733                 }
734
735                 if (a) {
736                         Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
737                         Con_DisplayList(list[2]);
738                 }
739         }
740
741         if (cmd) {
742                 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
743                 key_linepos = cmd_len + 1;
744                 if (c + v + a == 1) {
745                         key_lines[edit_line][key_linepos] = ' ';
746                         key_linepos++;
747                 }
748                 key_lines[edit_line][key_linepos] = 0;
749         }
750         for (i = 0; i < 3; i++)
751                 if (list[i])
752                         Mem_Free(list[i]);
753 }
754