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