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