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