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