]> icculus.org git repositories - btb/d2x.git/blob - main/console.c
prevent overflow of line buffer
[btb/d2x.git] / main / console.c
1 /*
2  * Code for controlling the console
3  *  Based on an old version of SDL_Console
4  *
5  *  Written By: Garrett Banuk <mongoose@mongeese.org>
6  *  Code Cleanup and heavily extended by: Clemens Wacha <reflex-2000@gmx.net>
7  *  Ported to use native Descent interfaces by: Bradley Bell <btb@icculus.org>
8  *
9  *  This is free, just be sure to give us credit when using it
10  *  in any of your programs.
11  */
12
13 #ifdef HAVE_CONFIG_H
14 #include <conf.h>
15 #endif
16
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <stdarg.h>
21 #ifndef _WIN32_WCE
22 #include <fcntl.h>
23 #endif
24 #include <ctype.h>
25
26 #include "console.h"
27
28 #include "u_mem.h"
29 #include "gr.h"
30 #include "timer.h"
31 #include "pstypes.h"
32 #include "error.h"
33 #include "cvar.h"
34 #include "gamefont.h"
35 #include "pcx.h"
36 #include "cfile.h"
37
38
39 #ifndef __MSDOS__
40 int text_console_enabled = 1;
41 #else
42 int isvga();
43 #define text_console_enabled (!isvga())
44 #endif
45
46
47 /* Console specific cvars */
48 /* How discriminating we are about which messages are displayed */
49 cvar_t con_threshold = {"con_threshold", "0",};
50
51 /* Private console stuff */
52 #define CON_NUM_LINES 40
53
54 #define FG_COLOR    grd_curcanv->cv_font_fg_color
55 #define get_msecs() approx_fsec_to_msec(timer_get_approx_seconds())
56
57 #define CON_BG_HIRES (cfexist("scoresb.pcx")?"scoresb.pcx":"scores.pcx")
58 #define CON_BG_LORES (cfexist("scores.pcx")?"scores.pcx":"scoresb.pcx") // Mac datafiles only have scoresb.pcx
59 #define CON_BG ((SWIDTH>=640)?CON_BG_HIRES:CON_BG_LORES)
60
61
62 /* our one console */
63 static ConsoleInformation Console;
64 #define console (&Console)
65
66 /* console is ready to be written to */
67 static int con_initialized;
68
69
70 /* Internals */
71 void CON_UpdateOffset(void);
72 /*! Frees all the memory loaded by the console */
73 void CON_Free(void);
74 #if 0
75 /*! Sets the alpha channel of an SDL_Surface to the specified value (0 - transparend,
76  255 - opaque). Use this function also for OpenGL. */
77 void CON_Alpha(unsigned char alpha);
78 /*! Internal: Sets the alpha channel of an SDL_Surface to the specified value.
79  Preconditions: the surface in question is RGBA. 0 <= a <= 255, where 0 is transparent and 255 opaque */
80 void CON_AlphaGL(SDL_Surface *s, int alpha);
81 /*! Sets a background image for the console */
82 #endif
83 int CON_Background(grs_bitmap *image);
84 /*! Sets font info for the console */
85 void CON_Font(grs_font *font, int fg, int bg);
86 /*! Modify the prompt of the console */
87 void CON_SetPrompt(char* newprompt);
88 /*! Set the key, that invokes a CON_Hide() after press. default is ESCAPE and you can always hide using
89  ESCAPE and the HideKey. compared against event->key.keysym.sym !! */
90 void CON_SetHideKey(int key);
91 /*! Internal: executes the command typed in at the console (called if you press ENTER)*/
92 void CON_Execute(char* command);
93 /*! Internal: Gets called when TAB was pressed */
94 void CON_TabCompletion(void);
95 /*! Internal: makes newline (same as printf("\n") or CON_Out("\n") ) */
96 void CON_NewLineConsole(void);
97 /*! Internal: shift command history (the one you can switch with the up/down keys) */
98 void CON_NewLineCommand(void);
99 /*! Internal: updates console after resize etc. */
100 void CON_UpdateConsole(void);
101
102
103 /*! Internal: draws the commandline the user is typing in to the screen. called by update? */
104 void DrawCommandLine();
105
106 /*! Internal: Gets called if you press the LEFT key (move cursor left) */
107 void Cursor_Left(void);
108 /*! Internal: Gets called if you press the RIGHT key (move cursor right) */
109 void Cursor_Right(void);
110 /*! Internal: Gets called if you press the HOME key (move cursor to the beginning
111         of the line */
112 void Cursor_Home(void);
113 /*! Internal: Gets called if you press the END key (move cursor to the end of the line*/
114 void Cursor_End(void);
115 /*! Internal: Called if you press DELETE (deletes character under the cursor) */
116 void Cursor_Del(void);
117 /*! Internal: Called if you press BACKSPACE (deletes character left of cursor) */
118 void Cursor_BSpace(void);
119 /*! Internal: Called if you type in a character (add the char to the command) */
120 void Cursor_Add(int event);
121
122 /*! Internal: Called if you press Ctrl-C (deletes the commandline) */
123 void Clear_Command(void);
124 /*! Internal: Called if you press Ctrl-L (deletes the History) */
125 void Clear_History(void);
126
127 /*! Internal: Called if you press UP key (switches through recent typed in commands */
128 void Command_Up(void);
129 /*! Internal: Called if you press DOWN key (switches through recent typed in commands */
130 void Command_Down(void);
131
132
133 /*  Takes keys from the keyboard and inputs them to the console
134  If the event was not handled (i.e. WM events or unknown ctrl-shift
135  sequences) the function returns the event for further processing. */
136 int CON_Events(int event)
137 {
138         if(!CON_isVisible())
139                 return event;
140         
141         if(event & KEY_CTRLED)
142         {
143                 //CTRL pressed
144                 switch(event & ~KEY_CTRLED)
145                 {
146                         case KEY_A:
147                                 Cursor_Home();
148                                 break;
149                         case KEY_E:
150                                 Cursor_End();
151                                 break;
152                         case KEY_C:
153                                 Clear_Command();
154                                 break;
155                         case KEY_L:
156                                 Clear_History();
157                                 CON_UpdateConsole();
158                                 break;
159                         default:
160                                 return event;
161                 }
162         }
163         else if(event & KEY_ALTED)
164         {
165                 //the console does not handle ALT combinations!
166                 return event;
167         }
168         else
169         {
170                 //first of all, check if the console hide key was pressed
171                 if(event == console->HideKey)
172                 {
173                         CON_Hide();
174                         return 0;
175                 }
176                 switch (event & 0xff)
177                 {
178                         case KEY_LSHIFT:
179                         case KEY_RSHIFT:
180                                 return event;
181                         case KEY_HOME:
182                                 if(event & KEY_SHIFTED)
183                                 {
184                                         console->ConsoleScrollBack = console->LineBuffer-1;
185                                         CON_UpdateConsole();
186                                 } else {
187                                         Cursor_Home();
188                                 }
189                                 break;
190                         case KEY_END:
191                                 if(event & KEY_SHIFTED)
192                                 {
193                                         console->ConsoleScrollBack = 0;
194                                         CON_UpdateConsole();
195                                 } else {
196                                         Cursor_End();
197                                 }
198                                 break;
199                         case KEY_PAGEUP:
200                                 console->ConsoleScrollBack += CON_LINE_SCROLL;
201                                 if(console->ConsoleScrollBack > console->LineBuffer-1)
202                                         console->ConsoleScrollBack = console->LineBuffer-1;
203                                 
204                                 CON_UpdateConsole();
205                                 break;
206                         case KEY_PAGEDOWN:
207                                 console->ConsoleScrollBack -= CON_LINE_SCROLL;
208                                 if(console->ConsoleScrollBack < 0)
209                                         console->ConsoleScrollBack = 0;
210                                 CON_UpdateConsole();
211                                 break;
212                         case KEY_UP:
213                                 Command_Up();
214                                 break;
215                         case KEY_DOWN:
216                                 Command_Down();
217                                 break;
218                         case KEY_LEFT:
219                                 Cursor_Left();
220                                 break;
221                         case KEY_RIGHT:
222                                 Cursor_Right();
223                                 break;
224                         case KEY_BACKSP:
225                                 Cursor_BSpace();
226                                 break;
227                         case KEY_DELETE:
228                                 Cursor_Del();
229                                 break;
230                         case KEY_INSERT:
231                                 console->InsMode = 1-console->InsMode;
232                                 break;
233                         case KEY_TAB:
234                                 CON_TabCompletion();
235                                 break;
236                         case KEY_ENTER:
237                                 if(strlen(console->Command) > 0) {
238                                         CON_NewLineCommand();
239                                         
240                                         // copy the input into the past commands strings
241                                         strcpy(console->CommandLines[0], console->Command);
242                                         
243                                         // display the command including the prompt
244                                         CON_Out("%s%s", console->Prompt, console->Command);
245                                         CON_UpdateConsole();
246                                         
247                                         CON_Execute(console->Command);
248                                         
249                                         Clear_Command();
250                                         console->CommandScrollBack = -1;
251                                 }
252                                 break;
253                         case KEY_LAPOSTRO:
254                                 //deactivate Console
255                                 CON_Hide();
256                                 return 0;
257                         default:
258                                 if (key_to_ascii(event) == 255)
259                                         break;
260                                 if(console->InsMode)
261                                         Cursor_Add(event);
262                                 else {
263                                         Cursor_Add(event);
264                                         Cursor_Del();
265                                 }
266                 }
267         }
268         return 0;
269 }
270
271 #if 0
272 /* CON_AlphaGL() -- sets the alpha channel of an SDL_Surface to the
273  * specified value.  Preconditions: the surface in question is RGBA.
274  * 0 <= a <= 255, where 0 is transparent and 255 is opaque. */
275 void CON_AlphaGL(SDL_Surface *s, int alpha) {
276         Uint8 val;
277         int x, y, w, h;
278         Uint32 pixel;
279         Uint8 r, g, b, a;
280         SDL_PixelFormat *format;
281         static char errorPrinted = 0;
282         
283         
284         /* debugging assertions -- these slow you down, but hey, crashing sucks */
285         if(!s) {
286                 PRINT_ERROR("NULL Surface passed to CON_AlphaGL\n");
287                 return;
288         }
289         
290         /* clamp alpha value to 0...255 */
291         if(alpha < SDL_ALPHA_TRANSPARENT)
292                 val = SDL_ALPHA_TRANSPARENT;
293         else if(alpha > SDL_ALPHA_OPAQUE)
294                 val = SDL_ALPHA_OPAQUE;
295         else
296                 val = alpha;
297         
298         /* loop over alpha channels of each pixel, setting them appropriately. */
299         w = s->w;
300         h = s->h;
301         format = s->format;
302         switch (format->BytesPerPixel) {
303                 case 2:
304                         /* 16-bit surfaces don't seem to support alpha channels. */
305                         if(!errorPrinted) {
306                                 errorPrinted = 1;
307                                 PRINT_ERROR("16-bit SDL surfaces do not support alpha-blending under OpenGL.\n");
308                         }
309                         break;
310                 case 4: {
311                         /* we can do this very quickly in 32-bit mode.  24-bit is more
312                          * difficult.  And since 24-bit mode is reall the same as 32-bit,
313                          * so it usually ends up taking this route too.  Win!  Unroll loop
314                          * and use pointer arithmetic for extra speed. */
315                         int numpixels = h * (w << 2);
316                         Uint8 *pix = (Uint8 *) (s->pixels);
317                         Uint8 *last = pix + numpixels;
318                         Uint8 *pixel;
319                         if((numpixels & 0x7) == 0)
320                                 for(pixel = pix + 3; pixel < last; pixel += 32)
321                                         *pixel = *(pixel + 4) = *(pixel + 8) = *(pixel + 12) = *(pixel + 16) = *(pixel + 20) = *(pixel + 24) = *(pixel + 28) = val;
322                         else
323                                 for(pixel = pix + 3; pixel < last; pixel += 4)
324                                         *pixel = val;
325                         break;
326                 }
327                 default:
328                         /* we have no choice but to do this slowly.  <sigh> */
329                         for(y = 0; y < h; ++y)
330                                 for(x = 0; x < w; ++x) {
331                                         char print = 0;
332                                         /* Lock the surface for direct access to the pixels */
333                                         if(SDL_MUSTLOCK(s) && SDL_LockSurface(s) < 0) {
334                                                 PRINT_ERROR("Can't lock surface: ");
335                                                 fprintf(stderr, "%s\n", SDL_GetError());
336                                                 return;
337                                         }
338                                         pixel = DT_GetPixel(s, x, y);
339                                         if(x == 0 && y == 0)
340                                                 print = 1;
341                                         SDL_GetRGBA(pixel, format, &r, &g, &b, &a);
342                                         pixel = SDL_MapRGBA(format, r, g, b, val);
343                                         SDL_GetRGBA(pixel, format, &r, &g, &b, &a);
344                                         DT_PutPixel(s, x, y, pixel);
345                                         
346                                         /* unlock surface again */
347                                         if(SDL_MUSTLOCK(s))
348                                                 SDL_UnlockSurface(s);
349                                 }
350                         break;
351         }
352 }
353 #endif
354
355
356 /* Updates the console buffer */
357 void CON_UpdateConsole(void) {
358         int loop;
359         int loop2;
360         int Screenlines;
361         grs_canvas *canv_save;
362         short orig_color;
363         
364         /* Due to the Blits, the update is not very fast: So only update if it's worth it */
365         if(!CON_isVisible())
366                 return;
367         
368         Screenlines = console->ConsoleSurface->cv_h / (CON_LINE_SPACE + console->ConsoleSurface->cv_font->ft_h);
369         
370         canv_save = grd_curcanv;
371         gr_set_current_canvas(console->ConsoleSurface);
372         
373 #if 0
374         SDL_FillRect(console->ConsoleSurface, NULL, SDL_MapRGBA(console->ConsoleSurface->format, 0, 0, 0, console->ConsoleAlpha));
375 #else
376         //gr_rect(0,0,
377 #endif
378         
379 #if 0
380         if(grd_curscreen->flags & SDL_OPENGLBLIT)
381                 SDL_SetAlpha(console->ConsoleSurface, 0, SDL_ALPHA_OPAQUE);
382 #endif
383         
384         /* draw the background image if there is one */
385         if(console->BackgroundImage)
386                 gr_bitmap(0, 0, console->BackgroundImage);
387         
388         /* Draw the text from the back buffers, calculate in the scrollback from the user
389          * this is a normal SDL software-mode blit, so we need to temporarily set the ColorKey
390          * for the font, and then clear it when we're done.
391          */
392 #if 0
393         if((grd_curscreen->flags & SDL_OPENGLBLIT) && (grd_curscreen->format->BytesPerPixel > 2)) {
394                 Uint32 *pix = (Uint32 *) (CurrentFont->FontSurface->pixels);
395                 SDL_SetColorKey(CurrentFont->FontSurface, SDL_SRCCOLORKEY, *pix);
396         }
397 #endif
398         
399         //now draw text from last but second line to top
400         for(loop = 0; loop < Screenlines-1 && loop < console->LineBuffer - console->ConsoleScrollBack; loop++) {
401                 if(console->ConsoleScrollBack != 0 && loop == 0)
402                         for(loop2 = 0; loop2 < (console->VChars / 5) + 1; loop2++)
403                         {
404                                 orig_color = FG_COLOR;
405                                 gr_string(CON_CHAR_BORDER + (loop2*5*console->ConsoleSurface->cv_font->ft_w), (Screenlines - loop - 2) * (CON_LINE_SPACE + console->ConsoleSurface->cv_font->ft_h), CON_SCROLL_INDICATOR);
406                                 FG_COLOR = orig_color;
407                         }
408                 else
409                 {
410                         orig_color = FG_COLOR;
411                         gr_string(CON_CHAR_BORDER, (Screenlines - loop - 2) * (CON_LINE_SPACE + console->ConsoleSurface->cv_font->ft_h), console->ConsoleLines[console->ConsoleScrollBack + loop]);
412                         FG_COLOR = orig_color;
413                 }
414         }
415         
416         gr_set_current_canvas(canv_save);
417         
418 #if 0
419         if(grd_curscreen->flags & SDL_OPENGLBLIT)
420                 SDL_SetColorKey(CurrentFont->FontSurface, 0, 0);
421 #endif
422 }
423
424 void CON_UpdateOffset(void) {
425         switch(console->Visible) {
426                 case CON_CLOSING:
427                         console->RaiseOffset -= CON_OPENCLOSE_SPEED;
428                         if(console->RaiseOffset <= 0) {
429                                 console->RaiseOffset = 0;
430                                 console->Visible = CON_CLOSED;
431                         }
432                         break;
433                 case CON_OPENING:
434                         console->RaiseOffset += CON_OPENCLOSE_SPEED;
435                         if(console->RaiseOffset >= console->ConsoleSurface->cv_h) {
436                                 console->RaiseOffset = console->ConsoleSurface->cv_h;
437                                 console->Visible = CON_OPEN;
438                         }
439                         break;
440                 case CON_OPEN:
441                 case CON_CLOSED:
442                         break;
443         }
444 }
445
446 /* Draws the console buffer to the screen if the console is "visible" */
447 void CON_DrawConsole(void) {
448         grs_canvas *canv_save;
449         grs_bitmap *clip;
450         
451         /* only draw if console is visible: here this means, that the console is not CON_CLOSED */
452         if(console->Visible == CON_CLOSED)
453                 return;
454         
455         /* Update the scrolling offset */
456         CON_UpdateOffset();
457         
458         /* Update the command line since it has a blinking cursor */
459         DrawCommandLine();
460         
461 #if 0
462         /* before drawing, make sure the alpha channel of the console surface is set
463          * properly.  (sigh) I wish we didn't have to do this every frame... */
464         if(grd_curscreen->flags & SDL_OPENGLBLIT)
465                 CON_AlphaGL(console->ConsoleSurface, console->ConsoleAlpha);
466 #endif
467         
468         canv_save = grd_curcanv;
469         gr_set_current_canvas(&grd_curscreen->sc_canvas);
470         
471         clip = gr_create_sub_bitmap(&console->ConsoleSurface->cv_bitmap, 0, console->ConsoleSurface->cv_h - console->RaiseOffset, console->ConsoleSurface->cv_w, console->RaiseOffset);
472         
473         gr_bitmap(0, 0, clip);
474         gr_free_sub_bitmap(clip);
475         
476 #if 0
477         if(grd_curscreen->flags & SDL_OPENGLBLIT)
478                 SDL_UpdateRects(grd_curscreen, 1, &DestRect);
479 #endif
480         
481         gr_set_current_canvas(canv_save);
482 }
483
484
485 /* Initializes the console */
486 void CON_Init()
487 {
488         int loop;
489
490         console->Visible = CON_CLOSED;
491         console->RaiseOffset = 0;
492         console->ConsoleLines = NULL;
493         console->CommandLines = NULL;
494         console->TotalConsoleLines = 0;
495         console->ConsoleScrollBack = 0;
496         console->TotalCommands = 0;
497         console->BackgroundImage = NULL;
498 #if 0
499         console->ConsoleAlpha = SDL_ALPHA_OPAQUE;
500 #endif
501         console->Offset = 0;
502         console->InsMode = 1;
503         console->CursorPos = 0;
504         console->CommandScrollBack = 0;
505         console->Prompt = CON_DEFAULT_PROMPT;
506         console->HideKey = CON_DEFAULT_HIDEKEY;
507
508         /* load the console surface */
509         console->ConsoleSurface = NULL;
510
511         /* Load the dirty rectangle for user input */
512         console->InputBackground = NULL;
513
514         console->VChars = CON_CHARS_PER_LINE - 1;
515         console->LineBuffer = CON_NUM_LINES;
516
517         console->ConsoleLines = (char **)d_malloc(sizeof(char *) * console->LineBuffer);
518         console->CommandLines = (char **)d_malloc(sizeof(char *) * console->LineBuffer);
519         for(loop = 0; loop <= console->LineBuffer - 1; loop++) {
520                 console->ConsoleLines[loop] = (char *)d_calloc(CON_CHARS_PER_LINE, sizeof(char));
521                 console->CommandLines[loop] = (char *)d_calloc(CON_CHARS_PER_LINE, sizeof(char));
522         }
523         memset(console->Command, 0, CON_CHARS_PER_LINE);
524         memset(console->LCommand, 0, CON_CHARS_PER_LINE);
525         memset(console->RCommand, 0, CON_CHARS_PER_LINE);
526         memset(console->VCommand, 0, CON_CHARS_PER_LINE);
527
528         cmd_init();
529
530         /* Initialise the cvars */
531         cvar_registervariable (&con_threshold);
532
533         con_initialized = 1;
534
535         atexit(CON_Free);
536 }
537
538
539 void gr_init_bitmap_alloc( grs_bitmap *bm, int mode, int x, int y, int w, int h, int bytesperline);
540 void CON_InitGFX(int w, int h)
541 {
542         int pcx_error;
543         grs_bitmap bmp;
544         ubyte pal[256*3];
545
546         if (console->ConsoleSurface) {
547                 /* resize console surface */
548                 gr_free_bitmap_data(&console->ConsoleSurface->cv_bitmap);
549                 gr_init_bitmap_alloc(&console->ConsoleSurface->cv_bitmap, BM_LINEAR, 0, 0, w, h, w);
550         } else {
551                 /* load the console surface */
552                 console->ConsoleSurface = gr_create_canvas(w, h);
553         }
554
555         /* Load the consoles font */
556         CON_Font(SMALL_FONT, gr_getcolor(63,63,63), -1);
557
558         /* make sure that the size of the console is valid */
559         if(w > grd_curscreen->sc_w || w < console->ConsoleSurface->cv_font->ft_w * 32)
560                 w = grd_curscreen->sc_w;
561         if(h > grd_curscreen->sc_h || h < console->ConsoleSurface->cv_font->ft_h)
562                 h = grd_curscreen->sc_h;
563
564         /* Load the dirty rectangle for user input */
565         if (console->InputBackground)
566                 gr_free_bitmap(console->InputBackground);
567         console->InputBackground = gr_create_bitmap(w, console->ConsoleSurface->cv_font->ft_h);
568 #if 0
569         SDL_FillRect(console->InputBackground, NULL, SDL_MapRGBA(console->ConsoleSurface->format, 0, 0, 0, SDL_ALPHA_OPAQUE));
570 #endif
571         
572         /* calculate the number of visible characters in the command line */
573         console->VChars = (w - CON_CHAR_BORDER) / console->ConsoleSurface->cv_font->ft_w;
574         if(console->VChars >= CON_CHARS_PER_LINE)
575                 console->VChars = CON_CHARS_PER_LINE - 1;
576         
577         gr_init_bitmap_data(&bmp);
578         pcx_error = pcx_read_bitmap(CON_BG, &bmp, BM_LINEAR, pal);
579         Assert(pcx_error == PCX_ERROR_NONE);
580         gr_remap_bitmap_good(&bmp, pal, -1, -1);
581         CON_Background(&bmp);
582         gr_free_bitmap_data(&bmp);
583 }
584
585
586 /* Makes the console visible */
587 void CON_Show(void) {
588         console->Visible = CON_OPENING;
589         CON_UpdateConsole();
590 }
591
592 /* Hides the console (make it invisible) */
593 void CON_Hide(void) {
594         console->Visible = CON_CLOSING;
595 }
596
597 /* tells wether the console is visible or not */
598 int CON_isVisible(void) {
599         return((console->Visible == CON_OPEN) || (console->Visible == CON_OPENING));
600 }
601
602 /* Frees all the memory loaded by the console */
603 void CON_Free(void) {
604         int i;
605         
606         for(i = 0; i <= console->LineBuffer - 1; i++) {
607                 d_free(console->ConsoleLines[i]);
608                 d_free(console->CommandLines[i]);
609         }
610         d_free(console->ConsoleLines);
611         d_free(console->CommandLines);
612         
613         console->ConsoleLines = NULL;
614         console->CommandLines = NULL;
615         
616         gr_free_canvas(console->ConsoleSurface);
617         console->ConsoleSurface = NULL;
618         
619         if (console->BackgroundImage)
620                 gr_free_bitmap(console->BackgroundImage);
621         console->BackgroundImage = NULL;
622         
623         gr_free_bitmap(console->InputBackground);
624         console->InputBackground = NULL;
625
626         con_initialized = 0;
627 }
628
629
630 /* Increments the console lines */
631 void CON_NewLineConsole(void) {
632         int loop;
633         char* temp;
634         
635         temp = console->ConsoleLines[console->LineBuffer - 1];
636         
637         for(loop = console->LineBuffer - 1; loop > 0; loop--)
638                 console->ConsoleLines[loop] = console->ConsoleLines[loop - 1];
639         
640         console->ConsoleLines[0] = temp;
641         
642         memset(console->ConsoleLines[0], 0, CON_CHARS_PER_LINE);
643         if(console->TotalConsoleLines < console->LineBuffer - 1)
644                 console->TotalConsoleLines++;
645         
646         //Now adjust the ConsoleScrollBack
647         //dont scroll if not at bottom
648         if(console->ConsoleScrollBack != 0)
649                 console->ConsoleScrollBack++;
650         //boundaries
651         if(console->ConsoleScrollBack > console->LineBuffer-1)
652                 console->ConsoleScrollBack = console->LineBuffer-1;
653         
654 }
655
656
657 /* Increments the command lines */
658 void CON_NewLineCommand(void) {
659         int loop;
660         char *temp;
661         
662         temp  = console->CommandLines[console->LineBuffer - 1];
663         
664         
665         for(loop = console->LineBuffer - 1; loop > 0; loop--)
666                 console->CommandLines[loop] = console->CommandLines[loop - 1];
667         
668         console->CommandLines[0] = temp;
669         
670         memset(console->CommandLines[0], 0, CON_CHARS_PER_LINE);
671         if(console->TotalCommands < console->LineBuffer - 1)
672                 console->TotalCommands++;
673 }
674
675 /* Draws the command line the user is typing in to the screen */
676 /* completely rewritten by C.Wacha */
677 void DrawCommandLine() {
678         int x;
679         int commandbuffer;
680 #if 0
681         grs_font* CurrentFont;
682 #endif
683         static unsigned int LastBlinkTime = 0;  /* Last time the consoles cursor blinked */
684         static int LastCursorPos = 0;           // Last Cursor Position
685         static int Blink = 0;                   /* Is the cursor currently blinking */
686         grs_canvas *canv_save;
687         short orig_color;
688         
689         commandbuffer = console->VChars - strlen(console->Prompt)-1; // -1 to make cursor visible
690         
691 #if 0
692         CurrentFont = console->ConsoleSurface->cv_font;
693 #endif
694         
695         //Concatenate the left and right side to command
696         strcpy(console->Command, console->LCommand);
697         strncat(console->Command, console->RCommand, strlen(console->RCommand));
698         
699         //calculate display offset from current cursor position
700         if(console->Offset < console->CursorPos - commandbuffer)
701                 console->Offset = console->CursorPos - commandbuffer;
702         if(console->Offset > console->CursorPos)
703                 console->Offset = console->CursorPos;
704         
705         //first add prompt to visible part
706         strcpy(console->VCommand, console->Prompt);
707         
708         //then add the visible part of the command
709         strncat(console->VCommand, &console->Command[console->Offset], strlen(&console->Command[console->Offset]));
710         
711         //now display the result
712         
713 #if 0
714         //once again we're drawing text, so in OpenGL context we need to temporarily set up
715         //software-mode transparency.
716         if(grd_curscreen->flags & SDL_OPENGLBLIT) {
717                 Uint32 *pix = (Uint32 *) (CurrentFont->FontSurface->pixels);
718                 SDL_SetColorKey(CurrentFont->FontSurface, SDL_SRCCOLORKEY, *pix);
719         }
720 #endif
721         
722         canv_save = grd_curcanv;
723         gr_set_current_canvas(console->ConsoleSurface);
724         
725         //first of all restore InputBackground
726         gr_bitmap(0, console->ConsoleSurface->cv_h - console->ConsoleSurface->cv_font->ft_h, console->InputBackground);
727         
728         //now add the text
729         orig_color = FG_COLOR;
730         gr_string(CON_CHAR_BORDER, console->ConsoleSurface->cv_h - console->ConsoleSurface->cv_font->ft_h, console->VCommand);
731         FG_COLOR = orig_color;
732         
733         //at last add the cursor
734         //check if the blink period is over
735         if(get_msecs() > LastBlinkTime) {
736                 LastBlinkTime = get_msecs() + CON_BLINK_RATE;
737                 if(Blink)
738                         Blink = 0;
739                 else
740                         Blink = 1;
741         }
742         
743         //check if cursor has moved - if yes display cursor anyway
744         if(console->CursorPos != LastCursorPos) {
745                 LastCursorPos = console->CursorPos;
746                 LastBlinkTime = get_msecs() + CON_BLINK_RATE;
747                 Blink = 1;
748         }
749         
750         if(Blink) {
751                 int prompt_width, cmd_width, h, w;
752                 
753                 gr_get_string_size(console->Prompt, &prompt_width, &h, &w);
754                 gr_get_string_size(console->LCommand + console->Offset, &cmd_width, &h, &w);
755                 x = CON_CHAR_BORDER + prompt_width + cmd_width;
756                 orig_color = FG_COLOR;
757                 if(console->InsMode)
758                         gr_string(x, console->ConsoleSurface->cv_h - console->ConsoleSurface->cv_font->ft_h, CON_INS_CURSOR);
759                 else
760                         gr_string(x, console->ConsoleSurface->cv_h - console->ConsoleSurface->cv_font->ft_h, CON_OVR_CURSOR);
761                 FG_COLOR = orig_color;
762         }
763         
764         gr_set_current_canvas(canv_save);
765         
766         
767 #if 0
768         if(grd_curscreen->flags & SDL_OPENGLBLIT) {
769                 SDL_SetColorKey(CurrentFont->FontSurface, 0, 0);
770         }
771 #endif
772 }
773
774 #ifdef _MSC_VER
775 # define vsnprintf _vsnprintf
776 #endif
777
778 /* Outputs text to the console (in game), up to CON_CHARS_PER_LINE chars can be entered */
779 void CON_Out(const char *str, ...) {
780         va_list marker;
781         //keep some space free for stuff like CON_Out("blablabla %s", console->Command);
782         char temp[CON_CHARS_PER_LINE + 128];
783         char* ptemp;
784         
785         va_start(marker, str);
786         vsnprintf(temp, CON_CHARS_PER_LINE + 127, str, marker);
787         va_end(marker);
788         
789         ptemp = temp;
790         
791         //temp now contains the complete string we want to output
792         // the only problem is that temp is maybe longer than the console
793         // width so we have to cut it into several pieces
794         
795         if(console->ConsoleLines) {
796                 while(strlen(ptemp) > console->VChars) {
797                         CON_NewLineConsole();
798                         strncpy(console->ConsoleLines[0], ptemp, console->VChars);
799                         console->ConsoleLines[0][console->VChars] = '\0';
800                         ptemp = &ptemp[console->VChars];
801                 }
802                 CON_NewLineConsole();
803                 strncpy(console->ConsoleLines[0], ptemp, console->VChars);
804                 console->ConsoleLines[0][console->VChars] = '\0';
805                 CON_UpdateConsole();
806         }
807 }
808
809
810 #if 0
811 /* Sets the alpha level of the console, 0 turns off alpha blending */
812 void CON_Alpha(unsigned char alpha) {
813         /* store alpha as state! */
814         console->ConsoleAlpha = alpha;
815         
816         if((grd_curscreen->flags & SDL_OPENGLBLIT) == 0) {
817                 if(alpha == 0)
818                         SDL_SetAlpha(console->ConsoleSurface, 0, alpha);
819                 else
820                         SDL_SetAlpha(console->ConsoleSurface, SDL_SRCALPHA, alpha);
821         }
822         
823         //      CON_UpdateConsole();
824 }
825 #endif
826
827
828 /* Adds  background image to the console, scaled to size of console*/
829 int CON_Background(grs_bitmap *image)
830 {
831         /* Free the background from the console */
832         if (image == NULL) {
833                 if (console->BackgroundImage)
834                         gr_free_bitmap(console->BackgroundImage);
835                 console->BackgroundImage = NULL;
836 #if 0
837                 SDL_FillRect(console->InputBackground, NULL, SDL_MapRGBA(console->ConsoleSurface->format, 0, 0, 0, SDL_ALPHA_OPAQUE));
838 #endif
839                 return 0;
840         }
841         
842         /* Load a new background */
843         if (console->BackgroundImage)
844                 gr_free_bitmap(console->BackgroundImage);
845         console->BackgroundImage = gr_create_bitmap(console->ConsoleSurface->cv_w, console->ConsoleSurface->cv_h);
846         gr_bitmap_scale_to(image, console->BackgroundImage);
847         
848 #if 0
849         SDL_FillRect(console->InputBackground, NULL, SDL_MapRGBA(console->ConsoleSurface->format, 0, 0, 0, SDL_ALPHA_OPAQUE));
850 #endif
851         gr_bm_bitblt(console->BackgroundImage->bm_w, console->InputBackground->bm_h, 0, 0, 0, console->ConsoleSurface->cv_h - console->ConsoleSurface->cv_font->ft_h, console->BackgroundImage, console->InputBackground);
852         
853         return 0;
854 }
855
856 /* Sets font info for the console */
857 void CON_Font(grs_font *font, int fg, int bg)
858 {
859         grs_canvas *canv_save;
860         
861         canv_save = grd_curcanv;
862         gr_set_current_canvas(console->ConsoleSurface);
863         gr_set_curfont(font);
864         gr_set_fontcolor(fg, bg);
865         gr_set_current_canvas(canv_save);
866 }
867
868 void gr_init_bitmap_alloc( grs_bitmap *bm, int mode, int x, int y, int w, int h, int bytesperline);
869 /* resizes the console, has to reset alot of stuff
870  * returns 1 on error */
871 void CON_Resize(int w, int h)
872 {
873         /* make sure that the size of the console is valid */
874         if(w > grd_curscreen->sc_w || w < console->ConsoleSurface->cv_font->ft_w * 32)
875                 w = grd_curscreen->sc_w;
876         if(h > grd_curscreen->sc_h || h < console->ConsoleSurface->cv_font->ft_h)
877                 h = grd_curscreen->sc_h;
878         
879         /* resize console surface */
880         gr_free_bitmap_data(&console->ConsoleSurface->cv_bitmap);
881         gr_init_bitmap_alloc(&console->ConsoleSurface->cv_bitmap, BM_LINEAR, 0, 0, w, h, w);
882         
883         /* Load the dirty rectangle for user input */
884         gr_free_bitmap(console->InputBackground);
885         console->InputBackground = gr_create_bitmap(w, console->ConsoleSurface->cv_font->ft_h);
886         
887         /* Now reset some stuff dependent on the previous size */
888         console->ConsoleScrollBack = 0;
889         
890         /* Reload the background image (for the input text area) in the console */
891         if(console->BackgroundImage) {
892 #if 0
893                 SDL_FillRect(console->InputBackground, NULL, SDL_MapRGBA(console->ConsoleSurface->format, 0, 0, 0, SDL_ALPHA_OPAQUE));
894 #endif
895                 gr_bm_bitblt(console->BackgroundImage->bm_w, console->InputBackground->bm_h, 0, 0, 0, console->ConsoleSurface->cv_h - console->ConsoleSurface->cv_font->ft_h, console->BackgroundImage, console->InputBackground);
896         }
897         
898 #if 0
899         /* restore the alpha level */
900         CON_Alpha(console->ConsoleAlpha);
901 #endif
902 }
903
904 /* Sets the Prompt for console */
905 void CON_SetPrompt(char* newprompt) {
906         //check length so we can still see at least 1 char :-)
907         if(strlen(newprompt) < console->VChars)
908                 console->Prompt = d_strdup(newprompt);
909         else
910                 CON_Out("prompt too long. (max. %i chars)", console->VChars - 1);
911 }
912
913 /* Sets the key that deactivates (hides) the console. */
914 void CON_SetHideKey(int key) {
915         console->HideKey = key;
916 }
917
918 /* Executes the command entered */
919 void CON_Execute(char* command) {
920         cmd_parse(command);
921 }
922
923 void CON_TabCompletion(void) {
924         int i,j;
925         char* command;
926         
927         command = d_strdup(console->LCommand);
928         command = cmd_complete(command);
929         
930         if(!command)
931                 return; //no tab completion took place so return silently
932         
933         j = strlen(command);
934         if(j > CON_CHARS_PER_LINE - 2)
935                 j = CON_CHARS_PER_LINE-1;
936         
937         memset(console->LCommand, 0, CON_CHARS_PER_LINE);
938         console->CursorPos = 0;
939         
940         for(i = 0; i < j; i++) {
941                 console->CursorPos++;
942                 console->LCommand[i] = command[i];
943         }
944         //add a trailing space
945         console->CursorPos++;
946         console->LCommand[j] = ' ';
947         console->LCommand[j+1] = '\0';
948 }
949
950 void Cursor_Left(void) {
951         char temp[CON_CHARS_PER_LINE];
952         
953         if(console->CursorPos > 0) {
954                 console->CursorPos--;
955                 strcpy(temp, console->RCommand);
956                 strcpy(console->RCommand, &console->LCommand[strlen(console->LCommand)-1]);
957                 strcat(console->RCommand, temp);
958                 console->LCommand[strlen(console->LCommand)-1] = '\0';
959                 //CON_Out("L:%s, R:%s", console->LCommand, console->RCommand);
960         }
961 }
962
963 void Cursor_Right(void) {
964         char temp[CON_CHARS_PER_LINE];
965         
966         if(console->CursorPos < strlen(console->Command)) {
967                 console->CursorPos++;
968                 strncat(console->LCommand, console->RCommand, 1);
969                 strcpy(temp, console->RCommand);
970                 strcpy(console->RCommand, &temp[1]);
971                 //CON_Out("L:%s, R:%s", console->LCommand, console->RCommand);
972         }
973 }
974
975 void Cursor_Home(void) {
976         char temp[CON_CHARS_PER_LINE];
977         
978         console->CursorPos = 0;
979         strcpy(temp, console->RCommand);
980         strcpy(console->RCommand, console->LCommand);
981         strncat(console->RCommand, temp, strlen(temp));
982         memset(console->LCommand, 0, CON_CHARS_PER_LINE);
983 }
984
985 void Cursor_End(void) {
986         console->CursorPos = strlen(console->Command);
987         strncat(console->LCommand, console->RCommand, strlen(console->RCommand));
988         memset(console->RCommand, 0, CON_CHARS_PER_LINE);
989 }
990
991 void Cursor_Del(void) {
992         char temp[CON_CHARS_PER_LINE];
993         
994         if(strlen(console->RCommand) > 0) {
995                 strcpy(temp, console->RCommand);
996                 strcpy(console->RCommand, &temp[1]);
997         }
998 }
999
1000 void Cursor_BSpace(void) {
1001         if(console->CursorPos > 0) {
1002                 console->CursorPos--;
1003                 console->Offset--;
1004                 if(console->Offset < 0)
1005                         console->Offset = 0;
1006                 console->LCommand[strlen(console->LCommand)-1] = '\0';
1007         }
1008 }
1009
1010 void Cursor_Add(int event)
1011 {
1012         if(strlen(console->Command) < CON_CHARS_PER_LINE - 1)
1013         {
1014                 console->CursorPos++;
1015                 console->LCommand[strlen(console->LCommand)] = key_to_ascii(event);
1016                 console->LCommand[strlen(console->LCommand)] = '\0';
1017         }
1018 }
1019
1020 void Clear_Command(void) {
1021         console->CursorPos = 0;
1022         memset(console->VCommand, 0, CON_CHARS_PER_LINE);
1023         memset(console->Command, 0, CON_CHARS_PER_LINE);
1024         memset(console->LCommand, 0, CON_CHARS_PER_LINE);
1025         memset(console->RCommand, 0, CON_CHARS_PER_LINE);
1026 }
1027
1028 void Clear_History(void) {
1029         int loop;
1030         
1031         for(loop = 0; loop <= console->LineBuffer - 1; loop++)
1032                 memset(console->ConsoleLines[loop], 0, CON_CHARS_PER_LINE);
1033 }
1034
1035 void Command_Up(void) {
1036         if(console->CommandScrollBack < console->TotalCommands - 1) {
1037                 /* move back a line in the command strings and copy the command to the current input string */
1038                 console->CommandScrollBack++;
1039                 memset(console->RCommand, 0, CON_CHARS_PER_LINE);
1040                 console->Offset = 0;
1041                 strcpy(console->LCommand, console->CommandLines[console->CommandScrollBack]);
1042                 console->CursorPos = strlen(console->CommandLines[console->CommandScrollBack]);
1043                 CON_UpdateConsole();
1044         }
1045 }
1046
1047 void Command_Down(void) {
1048         if(console->CommandScrollBack > -1) {
1049                 /* move forward a line in the command strings and copy the command to the current input string */
1050                 console->CommandScrollBack--;
1051                 memset(console->RCommand, 0, CON_CHARS_PER_LINE);
1052                 memset(console->LCommand, 0, CON_CHARS_PER_LINE);
1053                 console->Offset = 0;
1054                 if(console->CommandScrollBack > -1)
1055                         strcpy(console->LCommand, console->CommandLines[console->CommandScrollBack]);
1056                 console->CursorPos = strlen(console->LCommand);
1057                 CON_UpdateConsole();
1058         }
1059 }
1060
1061 /* Print a message to the console */
1062 void con_printf(int priority, char *fmt, ...)
1063 {
1064         va_list arglist;
1065         char buffer[2048];
1066
1067         if (priority <= ((int)con_threshold.value))
1068         {
1069                 va_start (arglist, fmt);
1070                 vsprintf (buffer,  fmt, arglist);
1071                 va_end (arglist);
1072
1073                 if (con_initialized)
1074                         CON_Out(buffer);
1075
1076                 if (text_console_enabled)
1077                 {
1078                         /* Produce a sanitised version and send it to the standard output */
1079                         char *p1, *p2;
1080
1081                         p1 = p2 = buffer;
1082                         do
1083                                 switch (*p1)
1084                                 {
1085                                 case CC_COLOR:
1086                                 case CC_LSPACING:
1087                                         p1++;
1088                                 case CC_UNDERLINE:
1089                                         p1++;
1090                                         break;
1091                                 default:
1092                                         *p2++ = *p1++;
1093                                 }
1094                         while (*p1);
1095                         *p2 = 0;
1096
1097                         printf("%s", buffer);
1098                 }
1099         }
1100 }