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