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