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