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