added R_ScrollTexCoord2f (copies while texture coordinates with an offset)
[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 cvar_t scr_conalpha;
581 extern char engineversion[40];
582 void Con_DrawConsole (int lines)
583 {
584         int i, y, rows, j;
585         char *text;
586
587         if (lines <= 0)
588                 return;
589
590 // draw the background
591         DrawQ_Pic(0, lines - vid.conheight, "gfx/conback", vid.conwidth, vid.conheight, 1, 1, 1, scr_conalpha.value * lines / vid.conheight, 0);
592         DrawQ_String(vid.conwidth - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
593
594 // draw the text
595         con_vislines = lines;
596
597         rows = (lines-16)>>3;           // rows of text to draw
598         y = lines - 16 - (rows<<3);     // may start slightly negative
599
600         for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
601         {
602                 j = max(i - con_backscroll, 0);
603                 text = con_text + (j % con_totallines)*con_linewidth;
604
605                 DrawQ_String(0, y, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
606         }
607
608 // draw the input prompt, user text, and cursor if desired
609         Con_DrawInput ();
610 }
611
612 /*
613         Con_DisplayList
614
615         New function for tab-completion system
616         Added by EvilTypeGuy
617         MEGA Thanks to Taniwha
618
619 */
620 void Con_DisplayList(const char **list)
621 {
622         int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
623         const char **walk = list;
624
625         while (*walk) {
626                 len = strlen(*walk);
627                 if (len > maxlen)
628                         maxlen = len;
629                 walk++;
630         }
631         maxlen += 1;
632
633         while (*list) {
634                 len = strlen(*list);
635                 if (pos + maxlen >= width) {
636                         Con_Printf("\n");
637                         pos = 0;
638                 }
639
640                 Con_Printf("%s", *list);
641                 for (i = 0; i < (maxlen - len); i++)
642                         Con_Printf(" ");
643
644                 pos += maxlen;
645                 list++;
646         }
647
648         if (pos)
649                 Con_Printf("\n\n");
650 }
651
652 /*
653         Con_CompleteCommandLine
654
655         New function for tab-completion system
656         Added by EvilTypeGuy
657         Thanks to Fett erich@heintz.com
658         Thanks to taniwha
659
660 */
661 void Con_CompleteCommandLine (void)
662 {
663         const char *cmd = "", *s;
664         const char **list[3] = {0, 0, 0};
665         int c, v, a, i, cmd_len;
666
667         s = key_lines[edit_line] + 1;
668         // Count number of possible matches
669         c = Cmd_CompleteCountPossible(s);
670         v = Cvar_CompleteCountPossible(s);
671         a = Cmd_CompleteAliasCountPossible(s);
672
673         if (!(c + v + a))       // No possible matches
674                 return;
675
676         if (c + v + a == 1) {
677                 if (c)
678                         list[0] = Cmd_CompleteBuildList(s);
679                 else if (v)
680                         list[0] = Cvar_CompleteBuildList(s);
681                 else
682                         list[0] = Cmd_CompleteAliasBuildList(s);
683                 cmd = *list[0];
684                 cmd_len = strlen (cmd);
685         } else {
686                 if (c)
687                         cmd = *(list[0] = Cmd_CompleteBuildList(s));
688                 if (v)
689                         cmd = *(list[1] = Cvar_CompleteBuildList(s));
690                 if (a)
691                         cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
692
693                 cmd_len = strlen (s);
694                 do {
695                         for (i = 0; i < 3; i++) {
696                                 char ch = cmd[cmd_len];
697                                 const char **l = list[i];
698                                 if (l) {
699                                         while (*l && (*l)[cmd_len] == ch)
700                                                 l++;
701                                         if (*l)
702                                                 break;
703                                 }
704                         }
705                         if (i == 3)
706                                 cmd_len++;
707                 } while (i == 3);
708                 // 'quakebar'
709                 Con_Printf("\n\35");
710                 for (i = 0; i < con_linewidth - 4; i++)
711                         Con_Printf("\36");
712                 Con_Printf("\37\n");
713
714                 // Print Possible Commands
715                 if (c) {
716                         Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
717                         Con_DisplayList(list[0]);
718                 }
719
720                 if (v) {
721                         Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
722                         Con_DisplayList(list[1]);
723                 }
724
725                 if (a) {
726                         Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
727                         Con_DisplayList(list[2]);
728                 }
729         }
730
731         if (cmd) {
732                 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
733                 key_linepos = cmd_len + 1;
734                 if (c + v + a == 1) {
735                         key_lines[edit_line][key_linepos] = ' ';
736                         key_linepos++;
737                 }
738                 key_lines[edit_line][key_linepos] = 0;
739         }
740         for (i = 0; i < 3; i++)
741                 if (list[i])
742                         Mem_Free((void *)list[i]);
743 }
744