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