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