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