]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/framework/Console.cpp
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / framework / Console.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 void SCR_DrawTextLeftAlign( float &y, const char *text, ... ) id_attribute((format(printf,2,3)));
33 void SCR_DrawTextRightAlign( float &y, const char *text, ... ) id_attribute((format(printf,2,3)));
34
35 #define LINE_WIDTH                              78
36 #define NUM_CON_TIMES                   4
37 #define CON_TEXTSIZE                    0x30000
38 #define TOTAL_LINES                             (CON_TEXTSIZE / LINE_WIDTH)
39 #define CONSOLE_FIRSTREPEAT             200
40 #define CONSOLE_REPEAT                  100
41
42 #define COMMAND_HISTORY                 64
43
44 // the console will query the cvar and command systems for
45 // command completion information
46
47 class idConsoleLocal : public idConsole {
48 public:
49         virtual void            Init( void );
50         virtual void            Shutdown( void );
51         virtual void            LoadGraphics( void );
52         virtual bool            ProcessEvent( const sysEvent_t *event, bool forceAccept );
53         virtual bool            Active( void );
54         virtual void            ClearNotifyLines( void );
55         virtual void            Close( void );
56         virtual void            Print( const char *text );
57         virtual void            Draw( bool forceFullScreen );
58
59         void                            Dump( const char *toFile );
60         void                            Clear();
61
62         //============================
63
64         const idMaterial *      charSetShader;
65
66 private:
67         void                            KeyDownEvent( int key );
68
69         void                            Linefeed();
70
71         void                            PageUp();
72         void                            PageDown();
73         void                            Top();
74         void                            Bottom();
75
76         void                            DrawInput();
77         void                            DrawNotify();
78         void                            DrawSolidConsole( float frac );
79
80         void                            Scroll();
81         void                            SetDisplayFraction( float frac );
82         void                            UpdateDisplayFraction( void );
83
84         //============================
85
86         bool                            keyCatching;
87
88         short                           text[CON_TEXTSIZE];
89         int                                     current;                // line where next message will be printed
90         int                                     x;                              // offset in current line for next print
91         int                                     display;                // bottom of console displays this line
92         int                                     lastKeyEvent;   // time of last key event for scroll delay
93         int                                     nextKeyEvent;   // keyboard repeat rate
94
95         float                           displayFrac;    // approaches finalFrac at scr_conspeed
96         float                           finalFrac;              // 0.0 to 1.0 lines of console to display
97         int                                     fracTime;               // time of last displayFrac update
98
99         int                                     vislines;               // in scanlines
100
101         int                                     times[NUM_CON_TIMES];   // cls.realtime time the line was generated
102                                                                         // for transparent notify lines
103         idVec4                          color;
104
105         idEditField                     historyEditLines[COMMAND_HISTORY];
106
107         int                                     nextHistoryLine;// the last line in the history buffer, not masked
108         int                                     historyLine;    // the line being displayed from history buffer
109                                                                         // will be <= nextHistoryLine
110
111         idEditField                     consoleField;
112
113         static idCVar           con_speed;
114         static idCVar           con_notifyTime;
115         static idCVar           con_noPrint;
116
117         const idMaterial *      whiteShader;
118         const idMaterial *      consoleShader;
119 };
120
121 static idConsoleLocal localConsole;
122 idConsole       *console = &localConsole;
123
124 idCVar idConsoleLocal::con_speed( "con_speed", "3", CVAR_SYSTEM, "speed at which the console moves up and down" );
125 idCVar idConsoleLocal::con_notifyTime( "con_notifyTime", "3", CVAR_SYSTEM, "time messages are displayed onscreen when console is pulled up" );
126 #ifdef DEBUG
127 idCVar idConsoleLocal::con_noPrint( "con_noPrint", "0", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT, "print on the console but not onscreen when console is pulled up" );
128 #else
129 idCVar idConsoleLocal::con_noPrint( "con_noPrint", "1", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT, "print on the console but not onscreen when console is pulled up" );
130 #endif
131
132
133
134 /*
135 =============================================================================
136
137         Misc stats
138
139 =============================================================================
140 */
141
142 /*
143 ==================
144 SCR_DrawTextLeftAlign
145 ==================
146 */
147 void SCR_DrawTextLeftAlign( float &y, const char *text, ... ) {
148         char string[MAX_STRING_CHARS];
149         va_list argptr;
150         va_start( argptr, text );
151         idStr::vsnPrintf( string, sizeof( string ), text, argptr );
152         va_end( argptr );
153         renderSystem->DrawSmallStringExt( 0, y + 2, string, colorWhite, true, localConsole.charSetShader );
154         y += SMALLCHAR_HEIGHT + 4;
155 }
156
157 /*
158 ==================
159 SCR_DrawTextRightAlign
160 ==================
161 */
162 void SCR_DrawTextRightAlign( float &y, const char *text, ... ) {
163         char string[MAX_STRING_CHARS];
164         va_list argptr;
165         va_start( argptr, text );
166         int i = idStr::vsnPrintf( string, sizeof( string ), text, argptr );
167         va_end( argptr );
168         renderSystem->DrawSmallStringExt( 635 - i * SMALLCHAR_WIDTH, y + 2, string, colorWhite, true, localConsole.charSetShader );
169         y += SMALLCHAR_HEIGHT + 4;
170 }
171
172
173
174
175 /*
176 ==================
177 SCR_DrawFPS
178 ==================
179 */
180 #define FPS_FRAMES      4
181 float SCR_DrawFPS( float y ) {
182         char            *s;
183         int                     w;
184         static int      previousTimes[FPS_FRAMES];
185         static int      index;
186         int             i, total;
187         int             fps;
188         static  int     previous;
189         int             t, frameTime;
190
191         // don't use serverTime, because that will be drifting to
192         // correct for internet lag changes, timescales, timedemos, etc
193         t = Sys_Milliseconds();
194         frameTime = t - previous;
195         previous = t;
196
197         previousTimes[index % FPS_FRAMES] = frameTime;
198         index++;
199         if ( index > FPS_FRAMES ) {
200                 // average multiple frames together to smooth changes out a bit
201                 total = 0;
202                 for ( i = 0 ; i < FPS_FRAMES ; i++ ) {
203                         total += previousTimes[i];
204                 }
205                 if ( !total ) {
206                         total = 1;
207                 }
208                 fps = 10000 * FPS_FRAMES / total;
209                 fps = (fps + 5)/10;
210
211                 s = va( "%ifps", fps );
212                 w = strlen( s ) * BIGCHAR_WIDTH;
213
214                 renderSystem->DrawBigStringExt( 635 - w, idMath::FtoiFast( y ) + 2, s, colorWhite, true, localConsole.charSetShader);
215         }
216
217         return y + BIGCHAR_HEIGHT + 4;
218 }
219
220 /*
221 ==================
222 SCR_DrawMemoryUsage
223 ==================
224 */
225 float SCR_DrawMemoryUsage( float y ) {
226         memoryStats_t allocs, frees;
227         
228         Mem_GetStats( allocs );
229         SCR_DrawTextRightAlign( y, "total allocated memory: %4d, %4dkB", allocs.num, allocs.totalSize>>10 );
230
231         Mem_GetFrameStats( allocs, frees );
232         SCR_DrawTextRightAlign( y, "frame alloc: %4d, %4dkB  frame free: %4d, %4dkB", allocs.num, allocs.totalSize>>10, frees.num, frees.totalSize>>10 );
233
234         Mem_ClearFrameStats();
235
236         return y;
237 }
238
239 /*
240 ==================
241 SCR_DrawAsyncStats
242 ==================
243 */
244 float SCR_DrawAsyncStats( float y ) {
245         int i, outgoingRate, incomingRate;
246         float outgoingCompression, incomingCompression;
247
248         if ( idAsyncNetwork::server.IsActive() ) {
249
250                 SCR_DrawTextRightAlign( y, "server delay = %d msec", idAsyncNetwork::server.GetDelay() );
251                 SCR_DrawTextRightAlign( y, "total outgoing rate = %d KB/s", idAsyncNetwork::server.GetOutgoingRate() >> 10 );
252                 SCR_DrawTextRightAlign( y, "total incoming rate = %d KB/s", idAsyncNetwork::server.GetIncomingRate() >> 10 );
253
254                 for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
255
256                         outgoingRate = idAsyncNetwork::server.GetClientOutgoingRate( i );
257                         incomingRate = idAsyncNetwork::server.GetClientIncomingRate( i );
258                         outgoingCompression = idAsyncNetwork::server.GetClientOutgoingCompression( i );
259                         incomingCompression = idAsyncNetwork::server.GetClientIncomingCompression( i );
260
261                         if ( outgoingRate != -1 && incomingRate != -1 ) {
262                                 SCR_DrawTextRightAlign( y, "client %d: out rate = %d B/s (% -2.1f%%), in rate = %d B/s (% -2.1f%%)",
263                                                                                         i, outgoingRate, outgoingCompression, incomingRate, incomingCompression );
264                         }
265                 }
266
267                 idStr msg;
268                 idAsyncNetwork::server.GetAsyncStatsAvgMsg( msg );
269                 SCR_DrawTextRightAlign( y, msg.c_str() );
270
271         } else if ( idAsyncNetwork::client.IsActive() ) {
272
273                 outgoingRate = idAsyncNetwork::client.GetOutgoingRate();
274                 incomingRate = idAsyncNetwork::client.GetIncomingRate();
275                 outgoingCompression = idAsyncNetwork::client.GetOutgoingCompression();
276                 incomingCompression = idAsyncNetwork::client.GetIncomingCompression();
277
278                 if ( outgoingRate != -1 && incomingRate != -1 ) {
279                         SCR_DrawTextRightAlign( y, "out rate = %d B/s (% -2.1f%%), in rate = %d B/s (% -2.1f%%)",
280                                                                                 outgoingRate, outgoingCompression, incomingRate, incomingCompression );
281                 }
282
283                 SCR_DrawTextRightAlign( y, "packet loss = %d%%, client prediction = %d",
284                                                                         (int)idAsyncNetwork::client.GetIncomingPacketLoss(), idAsyncNetwork::client.GetPrediction() );
285
286                 SCR_DrawTextRightAlign( y, "predicted frames: %d", idAsyncNetwork::client.GetPredictedFrames() );
287
288         }
289
290         return y;
291 }
292
293 /*
294 ==================
295 SCR_DrawSoundDecoders
296 ==================
297 */
298 float SCR_DrawSoundDecoders( float y ) {
299         int index, numActiveDecoders;
300         soundDecoderInfo_t decoderInfo;
301
302         index = -1;
303         numActiveDecoders = 0;
304         while( ( index = soundSystem->GetSoundDecoderInfo( index, decoderInfo ) ) != -1 ) {
305                 int localTime = decoderInfo.current44kHzTime - decoderInfo.start44kHzTime;
306                 int sampleTime = decoderInfo.num44kHzSamples / decoderInfo.numChannels;
307                 int percent;
308                 if ( localTime > sampleTime ) {
309                         if ( decoderInfo.looping ) {
310                                 percent = ( localTime % sampleTime ) * 100 / sampleTime;
311                         } else {
312                                 percent = 100;
313                         }
314                 } else {
315                         percent = localTime * 100 / sampleTime;
316                 }
317                 SCR_DrawTextLeftAlign( y, "%3d: %3d%% (%1.2f) %s: %s (%dkB)", numActiveDecoders, percent, decoderInfo.lastVolume, decoderInfo.format.c_str(), decoderInfo.name.c_str(), decoderInfo.numBytes >> 10 );
318                 numActiveDecoders++;
319         }
320         return y;
321 }
322
323 //=========================================================================
324
325 /*
326 ==============
327 Con_Clear_f
328 ==============
329 */
330 static void Con_Clear_f( const idCmdArgs &args ) {
331         localConsole.Clear();
332 }
333
334 /*
335 ==============
336 Con_Dump_f
337 ==============
338 */
339 static void Con_Dump_f( const idCmdArgs &args ) {
340         if ( args.Argc() != 2 ) {
341                 common->Printf( "usage: conDump <filename>\n" );
342                 return;
343         }
344
345         idStr fileName = args.Argv(1);
346         fileName.DefaultFileExtension(".txt");
347
348         common->Printf( "Dumped console text to %s.\n", fileName.c_str() );
349
350         localConsole.Dump( fileName.c_str() );
351 }
352
353 /*
354 ==============
355 idConsoleLocal::Init
356 ==============
357 */
358 void idConsoleLocal::Init( void ) {
359         int             i;
360
361         keyCatching = false;
362
363         lastKeyEvent = -1;
364         nextKeyEvent = CONSOLE_FIRSTREPEAT;
365
366         consoleField.Clear();
367
368         consoleField.SetWidthInChars( LINE_WIDTH );
369
370         for ( i = 0 ; i < COMMAND_HISTORY ; i++ ) {
371                 historyEditLines[i].Clear();
372                 historyEditLines[i].SetWidthInChars( LINE_WIDTH );
373         }
374
375         cmdSystem->AddCommand( "clear", Con_Clear_f, CMD_FL_SYSTEM, "clears the console" );
376         cmdSystem->AddCommand( "conDump", Con_Dump_f, CMD_FL_SYSTEM, "dumps the console text to a file" );
377 }
378
379 /*
380 ==============
381 idConsoleLocal::Shutdown
382 ==============
383 */
384 void idConsoleLocal::Shutdown( void ) {
385         cmdSystem->RemoveCommand( "clear" );
386         cmdSystem->RemoveCommand( "conDump" );
387 }
388
389 /*
390 ==============
391 LoadGraphics
392
393 Can't be combined with init, because init happens before
394 the renderSystem is initialized
395 ==============
396 */
397 void idConsoleLocal::LoadGraphics() {
398         charSetShader = declManager->FindMaterial( "textures/bigchars" );
399         whiteShader = declManager->FindMaterial( "_white" );
400         consoleShader = declManager->FindMaterial( "console" );
401 }
402
403 /*
404 ================
405 idConsoleLocal::Active
406 ================
407 */
408 bool    idConsoleLocal::Active( void ) {
409         return keyCatching;
410 }
411
412 /*
413 ================
414 idConsoleLocal::ClearNotifyLines
415 ================
416 */
417 void    idConsoleLocal::ClearNotifyLines() {
418         int             i;
419
420         for ( i = 0 ; i < NUM_CON_TIMES ; i++ ) {
421                 times[i] = 0;
422         }
423 }
424
425 /*
426 ================
427 idConsoleLocal::Close
428 ================
429 */
430 void    idConsoleLocal::Close() {
431         keyCatching = false;
432         SetDisplayFraction( 0 );
433         displayFrac = 0;        // don't scroll to that point, go immediately
434         ClearNotifyLines();
435 }
436
437 /*
438 ================
439 idConsoleLocal::Clear
440 ================
441 */
442 void idConsoleLocal::Clear() {
443         int             i;
444
445         for ( i = 0 ; i < CON_TEXTSIZE ; i++ ) {
446                 text[i] = (idStr::ColorIndex(C_COLOR_CYAN)<<8) | ' ';
447         }
448
449         Bottom();               // go to end
450 }
451
452 /*
453 ================
454 idConsoleLocal::Dump
455
456 Save the console contents out to a file
457 ================
458 */
459 void idConsoleLocal::Dump( const char *fileName ) {
460         int             l, x, i;
461         short * line;
462         idFile *f;
463         char    buffer[LINE_WIDTH + 3];
464
465         f = fileSystem->OpenFileWrite( fileName );
466         if ( !f ) {
467                 common->Warning( "couldn't open %s", fileName );
468                 return;
469         }
470
471         // skip empty lines
472         l = current - TOTAL_LINES + 1;
473         if ( l < 0 ) {
474                 l = 0;
475         }
476         for ( ; l <= current ; l++ )
477         {
478                 line = text + ( l % TOTAL_LINES ) * LINE_WIDTH;
479                 for ( x = 0; x < LINE_WIDTH; x++ )
480                         if ( ( line[x] & 0xff ) > ' ' )
481                                 break;
482                 if ( x != LINE_WIDTH )
483                         break;
484         }
485
486         // write the remaining lines
487         for ( ; l <= current; l++ ) {
488                 line = text + ( l % TOTAL_LINES ) * LINE_WIDTH;
489                 for( i = 0; i < LINE_WIDTH; i++ ) {
490                         buffer[i] = line[i] & 0xff;
491                 }
492                 for ( x = LINE_WIDTH-1; x >= 0; x-- ) {
493                         if ( buffer[x] <= ' ' ) {
494                                 buffer[x] = 0;
495                         } else {
496                                 break;
497                         }
498                 }
499                 buffer[x+1] = '\r';
500                 buffer[x+2] = '\n';
501                 buffer[x+3] = 0;
502                 f->Write( buffer, strlen( buffer ) );
503         }
504
505         fileSystem->CloseFile( f );
506 }
507
508 /*
509 ================
510 idConsoleLocal::PageUp
511 ================
512 */
513 void idConsoleLocal::PageUp( void ) {
514         display -= 2;
515         if ( current - display >= TOTAL_LINES ) {
516                 display = current - TOTAL_LINES + 1;
517         }
518 }
519
520 /*
521 ================
522 idConsoleLocal::PageDown
523 ================
524 */
525 void idConsoleLocal::PageDown( void ) {
526         display += 2;
527         if ( display > current ) {
528                 display = current;
529         }
530 }
531
532 /*
533 ================
534 idConsoleLocal::Top
535 ================
536 */
537 void idConsoleLocal::Top( void ) {
538         display = 0;
539 }
540
541 /*
542 ================
543 idConsoleLocal::Bottom
544 ================
545 */
546 void idConsoleLocal::Bottom( void ) {
547         display = current;
548 }
549
550
551 /*
552 =============================================================================
553
554 CONSOLE LINE EDITING
555
556 ==============================================================================
557 */
558
559 /*
560 ====================
561 KeyDownEvent
562
563 Handles history and console scrollback
564 ====================
565 */
566 void idConsoleLocal::KeyDownEvent( int key ) {
567         
568         // Execute F key bindings
569         if ( key >= K_F1 && key <= K_F12 ) {
570                 idKeyInput::ExecKeyBinding( key );
571                 return;
572         }
573
574         // ctrl-L clears screen
575         if ( key == 'l' && idKeyInput::IsDown( K_CTRL ) ) {
576                 Clear();
577                 return;
578         }
579
580         // enter finishes the line
581         if ( key == K_ENTER || key == K_KP_ENTER ) {
582
583                 common->Printf ( "]%s\n", consoleField.GetBuffer() );
584
585                 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, consoleField.GetBuffer() );      // valid command
586                 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "\n" );
587
588                 // copy line to history buffer
589                 historyEditLines[nextHistoryLine % COMMAND_HISTORY] = consoleField;
590                 nextHistoryLine++;
591                 historyLine = nextHistoryLine;
592
593                 consoleField.Clear();
594                 consoleField.SetWidthInChars( LINE_WIDTH );
595
596                 session->UpdateScreen();// force an update, because the command
597                                                                 // may take some time
598                 return;
599         }
600
601         // command completion
602
603         if ( key == K_TAB ) {
604                 consoleField.AutoComplete();
605                 return;
606         }
607
608         // command history (ctrl-p ctrl-n for unix style)
609
610         if ( ( key == K_UPARROW ) ||
611                  ( ( tolower(key) == 'p' ) && idKeyInput::IsDown( K_CTRL ) ) ) {
612                 if ( nextHistoryLine - historyLine < COMMAND_HISTORY && historyLine > 0 ) {
613                         historyLine--;
614                 }
615                 consoleField = historyEditLines[ historyLine % COMMAND_HISTORY ];
616                 return;
617         }
618
619         if ( ( key == K_DOWNARROW ) ||
620                  ( ( tolower( key ) == 'n' ) && idKeyInput::IsDown( K_CTRL ) ) ) {
621                 if ( historyLine == nextHistoryLine ) {
622                         return;
623                 }
624                 historyLine++;
625                 consoleField = historyEditLines[ historyLine % COMMAND_HISTORY ];
626                 return;
627         }
628
629         // console scrolling
630         if ( key == K_PGUP ) {
631                 PageUp();
632                 lastKeyEvent = eventLoop->Milliseconds();
633                 nextKeyEvent = CONSOLE_FIRSTREPEAT;
634                 return;
635         }
636
637         if ( key == K_PGDN ) {
638                 PageDown();
639                 lastKeyEvent = eventLoop->Milliseconds();
640                 nextKeyEvent = CONSOLE_FIRSTREPEAT;
641                 return;
642         }
643
644         if ( key == K_MWHEELUP ) {
645                 PageUp();
646                 return;
647         }
648
649         if ( key == K_MWHEELDOWN ) {
650                 PageDown();
651                 return;
652         }
653
654         // ctrl-home = top of console
655         if ( key == K_HOME && idKeyInput::IsDown( K_CTRL ) ) {
656                 Top();
657                 return;
658         }
659
660         // ctrl-end = bottom of console
661         if ( key == K_END && idKeyInput::IsDown( K_CTRL ) ) {
662                 Bottom();
663                 return;
664         }
665
666         // pass to the normal editline routine
667         consoleField.KeyDownEvent( key );
668 }
669
670 /*
671 ==============
672 Scroll
673 deals with scrolling text because we don't have key repeat
674 ==============
675 */
676 void idConsoleLocal::Scroll( ) {
677         if (lastKeyEvent == -1 || (lastKeyEvent+200) > eventLoop->Milliseconds()) {
678                 return;
679         }
680         // console scrolling
681         if ( idKeyInput::IsDown( K_PGUP ) ) {
682                 PageUp();
683                 nextKeyEvent = CONSOLE_REPEAT;
684                 return;
685         }
686
687         if ( idKeyInput::IsDown( K_PGDN ) ) {
688                 PageDown();
689                 nextKeyEvent = CONSOLE_REPEAT;
690                 return;
691         }
692 }
693
694 /*
695 ==============
696 SetDisplayFraction
697
698 Causes the console to start opening the desired amount.
699 ==============
700 */
701 void idConsoleLocal::SetDisplayFraction( float frac ) {
702         finalFrac = frac;
703         fracTime = com_frameTime;
704 }
705
706 /*
707 ==============
708 UpdateDisplayFraction
709
710 Scrolls the console up or down based on conspeed
711 ==============
712 */
713 void idConsoleLocal::UpdateDisplayFraction( void ) {
714         if ( con_speed.GetFloat() <= 0.1f ) {
715                 fracTime = com_frameTime;
716                 displayFrac = finalFrac;
717                 return;
718         }
719
720         // scroll towards the destination height
721         if ( finalFrac < displayFrac ) {
722                 displayFrac -= con_speed.GetFloat() * ( com_frameTime - fracTime ) * 0.001f;
723                 if ( finalFrac > displayFrac ) {
724                         displayFrac = finalFrac;
725                 }
726                 fracTime = com_frameTime;
727         } else if ( finalFrac > displayFrac ) {
728                 displayFrac += con_speed.GetFloat() * ( com_frameTime - fracTime ) * 0.001f;
729                 if ( finalFrac < displayFrac ) {
730                         displayFrac = finalFrac;
731                 }
732                 fracTime = com_frameTime;
733         }
734 }
735
736 /*
737 ==============
738 ProcessEvent
739 ==============
740 */
741 bool    idConsoleLocal::ProcessEvent( const sysEvent_t *event, bool forceAccept ) {
742         bool consoleKey;
743         consoleKey = event->evType == SE_KEY && ( event->evValue == Sys_GetConsoleKey( false ) || event->evValue == Sys_GetConsoleKey( true ) );
744
745 #if ID_CONSOLE_LOCK
746         // If the console's not already down, and we have it turned off, check for ctrl+alt
747         if ( !keyCatching && !com_allowConsole.GetBool() ) {
748                 if ( !idKeyInput::IsDown( K_CTRL ) || !idKeyInput::IsDown( K_ALT ) ) {
749                         consoleKey = false;
750                 }
751         }
752 #endif
753
754         // we always catch the console key event
755         if ( !forceAccept && consoleKey ) {
756                 // ignore up events
757                 if ( event->evValue2 == 0 ) {
758                         return true;
759                 }
760
761                 consoleField.ClearAutoComplete();
762
763                 // a down event will toggle the destination lines
764                 if ( keyCatching ) {
765                         Close();
766                         Sys_GrabMouseCursor( true );
767                         cvarSystem->SetCVarBool( "ui_chat", false );
768                 } else {
769                         consoleField.Clear();
770                         keyCatching = true;
771                         if ( idKeyInput::IsDown( K_SHIFT ) ) {
772                                 // if the shift key is down, don't open the console as much
773                                 SetDisplayFraction( 0.2f );
774                         } else {
775                                 SetDisplayFraction( 0.5f );
776                         }
777                         cvarSystem->SetCVarBool( "ui_chat", true );
778                 }
779                 return true;
780         }
781
782         // if we aren't key catching, dump all the other events
783         if ( !forceAccept && !keyCatching ) {
784                 return false;
785         }
786
787         // handle key and character events
788         if ( event->evType == SE_CHAR ) {
789                 // never send the console key as a character
790                 if ( event->evValue != Sys_GetConsoleKey( false ) && event->evValue != Sys_GetConsoleKey( true ) ) {
791                         consoleField.CharEvent( event->evValue );
792                 }
793                 return true;
794         }
795
796         if ( event->evType == SE_KEY ) {
797                 // ignore up key events
798                 if ( event->evValue2 == 0 ) {
799                         return true;
800                 }
801
802                 KeyDownEvent( event->evValue );
803                 return true;
804         }
805
806         // we don't handle things like mouse, joystick, and network packets
807         return false;
808 }
809
810 /*
811 ==============================================================================
812
813 PRINTING
814
815 ==============================================================================
816 */
817
818 /*
819 ===============
820 Linefeed
821 ===============
822 */
823 void idConsoleLocal::Linefeed() {
824         int             i;
825
826         // mark time for transparent overlay
827         if ( current >= 0 ) {
828                 times[current % NUM_CON_TIMES] = com_frameTime;
829         }
830
831         x = 0;
832         if ( display == current ) {
833                 display++;
834         }
835         current++;
836         for ( i = 0; i < LINE_WIDTH; i++ ) {
837                 text[(current%TOTAL_LINES)*LINE_WIDTH+i] = (idStr::ColorIndex(C_COLOR_CYAN)<<8) | ' ';
838         }
839 }
840
841
842 /*
843 ================
844 Print
845
846 Handles cursor positioning, line wrapping, etc
847 ================
848 */
849 void idConsoleLocal::Print( const char *txt ) {
850         int             y;
851         int             c, l;
852         int             color;
853
854 #ifdef ID_ALLOW_TOOLS
855         RadiantPrint( txt );
856
857         if( com_editors & EDITOR_MATERIAL ) {
858                 MaterialEditorPrintConsole(txt);
859         }
860 #endif
861
862         color = idStr::ColorIndex( C_COLOR_CYAN );
863
864         while ( (c = *(const unsigned char*)txt) != 0 ) {
865                 if ( idStr::IsColor( txt ) ) {
866                         if ( *(txt+1) == C_COLOR_DEFAULT ) {
867                                 color = idStr::ColorIndex( C_COLOR_CYAN );
868                         } else {
869                                 color = idStr::ColorIndex( *(txt+1) );
870                         }
871                         txt += 2;
872                         continue;
873                 }
874
875                 y = current % TOTAL_LINES;
876
877                 // if we are about to print a new word, check to see
878                 // if we should wrap to the new line
879                 if ( c > ' ' && ( x == 0 || text[y*LINE_WIDTH+x-1] <= ' ' ) ) {
880                         // count word length
881                         for (l=0 ; l< LINE_WIDTH ; l++) {
882                                 if ( txt[l] <= ' ') {
883                                         break;
884                                 }
885                         }
886
887                         // word wrap
888                         if (l != LINE_WIDTH && (x + l >= LINE_WIDTH) ) {
889                                 Linefeed();
890                         }
891                 }
892
893                 txt++;
894
895                 switch( c ) {
896                         case '\n':
897                                 Linefeed ();
898                                 break;
899                         case '\t':
900                                 do {
901                                         text[y*LINE_WIDTH+x] = (color << 8) | ' ';
902                                         x++;
903                                         if ( x >= LINE_WIDTH ) {
904                                                 Linefeed();
905                                                 x = 0;
906                                         }
907                                 } while ( x & 3 );
908                                 break;
909                         case '\r':
910                                 x = 0;
911                                 break;
912                         default:        // display character and advance
913                                 text[y*LINE_WIDTH+x] = (color << 8) | c;
914                                 x++;
915                                 if ( x >= LINE_WIDTH ) {
916                                         Linefeed();
917                                         x = 0;
918                                 }
919                                 break;
920                 }
921         }
922
923
924         // mark time for transparent overlay
925         if ( current >= 0 ) {
926                 times[current % NUM_CON_TIMES] = com_frameTime;
927         }
928 }
929
930
931 /*
932 ==============================================================================
933
934 DRAWING
935
936 ==============================================================================
937 */
938
939
940 /*
941 ================
942 DrawInput
943
944 Draw the editline after a ] prompt
945 ================
946 */
947 void idConsoleLocal::DrawInput() {
948         int y, autoCompleteLength;
949
950         y = vislines - ( SMALLCHAR_HEIGHT * 2 );
951
952         if ( consoleField.GetAutoCompleteLength() != 0 ) {
953                 autoCompleteLength = strlen( consoleField.GetBuffer() ) - consoleField.GetAutoCompleteLength();
954
955                 if ( autoCompleteLength > 0 ) {
956                         renderSystem->SetColor4( .8f, .2f, .2f, .45f );
957
958                         renderSystem->DrawStretchPic( 2 * SMALLCHAR_WIDTH + consoleField.GetAutoCompleteLength() * SMALLCHAR_WIDTH,
959                                                         y + 2, autoCompleteLength * SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT - 2, 0, 0, 0, 0, whiteShader );
960
961                 }
962         }
963
964         renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CYAN ) );
965
966         renderSystem->DrawSmallChar( 1 * SMALLCHAR_WIDTH, y, ']', localConsole.charSetShader );
967
968         consoleField.Draw(2 * SMALLCHAR_WIDTH, y, SCREEN_WIDTH - 3 * SMALLCHAR_WIDTH, true, charSetShader );
969 }
970
971
972 /*
973 ================
974 DrawNotify
975
976 Draws the last few lines of output transparently over the game top
977 ================
978 */
979 void idConsoleLocal::DrawNotify() {
980         int             x, v;
981         short   *text_p;
982         int             i;
983         int             time;
984         int             currentColor;
985
986         if ( con_noPrint.GetBool() ) {
987                 return;
988         }
989
990         currentColor = idStr::ColorIndex( C_COLOR_WHITE );
991         renderSystem->SetColor( idStr::ColorForIndex( currentColor ) );
992
993         v = 0;
994         for ( i = current-NUM_CON_TIMES+1; i <= current; i++ ) {
995                 if ( i < 0 ) {
996                         continue;
997                 }
998                 time = times[i % NUM_CON_TIMES];
999                 if ( time == 0 ) {
1000                         continue;
1001                 }
1002                 time = com_frameTime - time;
1003                 if ( time > con_notifyTime.GetFloat() * 1000 ) {
1004                         continue;
1005                 }
1006                 text_p = text + (i % TOTAL_LINES)*LINE_WIDTH;
1007                 
1008                 for ( x = 0; x < LINE_WIDTH; x++ ) {
1009                         if ( ( text_p[x] & 0xff ) == ' ' ) {
1010                                 continue;
1011                         }
1012                         if ( idStr::ColorIndex(text_p[x]>>8) != currentColor ) {
1013                                 currentColor = idStr::ColorIndex(text_p[x]>>8);
1014                                 renderSystem->SetColor( idStr::ColorForIndex( currentColor ) );
1015                         }
1016                         renderSystem->DrawSmallChar( (x+1)*SMALLCHAR_WIDTH, v, text_p[x] & 0xff, localConsole.charSetShader );
1017                 }
1018
1019                 v += SMALLCHAR_HEIGHT;
1020         }
1021
1022         renderSystem->SetColor( colorCyan );
1023 }
1024
1025 /*
1026 ================
1027 DrawSolidConsole
1028
1029 Draws the console with the solid background
1030 ================
1031 */
1032 void idConsoleLocal::DrawSolidConsole( float frac ) {
1033         int                             i, x;
1034         float                   y;
1035         int                             rows;
1036         short                   *text_p;
1037         int                             row;
1038         int                             lines;
1039         int                             currentColor;
1040
1041         lines = idMath::FtoiFast( SCREEN_HEIGHT * frac );
1042         if ( lines <= 0 ) {
1043                 return;
1044         }
1045
1046         if ( lines > SCREEN_HEIGHT ) {
1047                 lines = SCREEN_HEIGHT;
1048         }
1049
1050         // draw the background
1051         y = frac * SCREEN_HEIGHT - 2;
1052         if ( y < 1.0f ) {
1053                 y = 0.0f;
1054         } else {
1055                 renderSystem->DrawStretchPic( 0, 0, SCREEN_WIDTH, y, 0, 1.0f - displayFrac, 1, 1, consoleShader );
1056         }
1057
1058         renderSystem->SetColor( colorCyan );
1059         renderSystem->DrawStretchPic( 0, y, SCREEN_WIDTH, 2, 0, 0, 0, 0, whiteShader );
1060         renderSystem->SetColor( colorWhite );
1061
1062         // draw the version number
1063
1064         renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CYAN ) );
1065
1066         idStr version = va("%s.%i", ENGINE_VERSION, BUILD_NUMBER);
1067         i = version.Length();
1068
1069         for ( x = 0; x < i; x++ ) {
1070                 renderSystem->DrawSmallChar( SCREEN_WIDTH - ( i - x ) * SMALLCHAR_WIDTH, 
1071                         (lines-(SMALLCHAR_HEIGHT+SMALLCHAR_HEIGHT/2)), version[x], localConsole.charSetShader );
1072
1073         }
1074
1075
1076         // draw the text
1077         vislines = lines;
1078         rows = (lines-SMALLCHAR_WIDTH)/SMALLCHAR_WIDTH;         // rows of text to draw
1079
1080         y = lines - (SMALLCHAR_HEIGHT*3);
1081
1082         // draw from the bottom up
1083         if ( display != current ) {
1084                 // draw arrows to show the buffer is backscrolled
1085                 renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CYAN ) );
1086                 for ( x = 0; x < LINE_WIDTH; x += 4 ) {
1087                         renderSystem->DrawSmallChar( (x+1)*SMALLCHAR_WIDTH, idMath::FtoiFast( y ), '^', localConsole.charSetShader );
1088                 }
1089                 y -= SMALLCHAR_HEIGHT;
1090                 rows--;
1091         }
1092         
1093         row = display;
1094
1095         if ( x == 0 ) {
1096                 row--;
1097         }
1098
1099         currentColor = idStr::ColorIndex( C_COLOR_WHITE );
1100         renderSystem->SetColor( idStr::ColorForIndex( currentColor ) );
1101
1102         for ( i = 0; i < rows; i++, y -= SMALLCHAR_HEIGHT, row-- ) {
1103                 if ( row < 0 ) {
1104                         break;
1105                 }
1106                 if ( current - row >= TOTAL_LINES ) {
1107                         // past scrollback wrap point
1108                         continue;       
1109                 }
1110
1111                 text_p = text + (row % TOTAL_LINES)*LINE_WIDTH;
1112
1113                 for ( x = 0; x < LINE_WIDTH; x++ ) {
1114                         if ( ( text_p[x] & 0xff ) == ' ' ) {
1115                                 continue;
1116                         }
1117
1118                         if ( idStr::ColorIndex(text_p[x]>>8) != currentColor ) {
1119                                 currentColor = idStr::ColorIndex(text_p[x]>>8);
1120                                 renderSystem->SetColor( idStr::ColorForIndex( currentColor ) );
1121                         }
1122                         renderSystem->DrawSmallChar( (x+1)*SMALLCHAR_WIDTH, idMath::FtoiFast( y ), text_p[x] & 0xff, localConsole.charSetShader );
1123                 }
1124         }
1125
1126         // draw the input prompt, user text, and cursor if desired
1127         DrawInput();
1128
1129         renderSystem->SetColor( colorCyan );
1130 }
1131
1132
1133 /*
1134 ==============
1135 Draw
1136
1137 ForceFullScreen is used by the editor
1138 ==============
1139 */
1140 void    idConsoleLocal::Draw( bool forceFullScreen ) {
1141         float y = 0.0f;
1142
1143         if ( !charSetShader ) {
1144                 return;
1145         }
1146
1147         if ( forceFullScreen ) {
1148                 // if we are forced full screen because of a disconnect, 
1149                 // we want the console closed when we go back to a session state
1150                 Close();
1151                 // we are however catching keyboard input
1152                 keyCatching = true;
1153         }
1154
1155         Scroll();
1156
1157         UpdateDisplayFraction();
1158
1159         if ( forceFullScreen ) {
1160                 DrawSolidConsole( 1.0f );
1161         } else if ( displayFrac ) {
1162                 DrawSolidConsole( displayFrac );
1163         } else {
1164                 // only draw the notify lines if the developer cvar is set,
1165                 // or we are a debug build
1166                 if ( !con_noPrint.GetBool() ) {
1167                         DrawNotify();
1168                 }
1169         }
1170
1171         if ( com_showFPS.GetBool() ) {
1172                 y = SCR_DrawFPS( 0 );
1173         }
1174
1175         if ( com_showMemoryUsage.GetBool() ) {
1176                 y = SCR_DrawMemoryUsage( y );
1177         }
1178
1179         if ( com_showAsyncStats.GetBool() ) {
1180                 y = SCR_DrawAsyncStats( y );
1181         }
1182
1183         if ( com_showSoundDecoders.GetBool() ) {
1184                 y = SCR_DrawSoundDecoders( y );
1185         }
1186 }