2 * Code for controlling the console
3 * Based on an old version of SDL_Console
5 * Written By: Garrett Banuk <mongoose@mongeese.org>
6 * Code Cleanup and heavily extended by: Clemens Wacha <reflex-2000@gmx.net>
7 * Ported to use native Descent interfaces by: Bradley Bell <btb@icculus.org>
9 * This is free, just be sure to give us credit when using it
10 * in any of your programs.
28 #define FG_COLOR grd_curcanv->cv_font_fg_color
29 #define get_msecs() approx_fsec_to_msec(timer_get_approx_seconds())
32 /* This contains a pointer to the "topmost" console. The console that
33 * is currently taking keyboard input. */
34 static ConsoleInformation *Topmost;
37 void CON_UpdateOffset(ConsoleInformation* console);
39 void CON_Destroy(ConsoleInformation *console);
40 /*! Frees all the memory loaded by the console */
41 void CON_Free(ConsoleInformation *console);
43 /*! Sets the alpha channel of an SDL_Surface to the specified value (0 - transparend,
44 255 - opaque). Use this function also for OpenGL. */
45 void CON_Alpha(ConsoleInformation *console, unsigned char alpha);
46 /*! Internal: Sets the alpha channel of an SDL_Surface to the specified value.
47 Preconditions: the surface in question is RGBA. 0 <= a <= 255, where 0 is transparent and 255 opaque */
48 void CON_AlphaGL(SDL_Surface *s, int alpha);
49 /*! Sets a background image for the console */
51 int CON_Background(ConsoleInformation *console, grs_bitmap *image);
52 /*! Sets font info for the console */
53 void CON_Font(ConsoleInformation *console, grs_font *font, int fg, int bg);
54 /*! Changes current position of the console */
55 void CON_Position(ConsoleInformation *console, int x, int y);
56 /*! Beams a console to another screen surface. Needed if you want to make a Video restart in your program. This
57 function first changes the OutputScreen Pointer then calls CON_Resize to adjust the new size. */
58 int CON_Transfer(ConsoleInformation* console, grs_screen* new_outputscreen, int x, int y, int w, int h);
59 /*! Give focus to a console. Make it the "topmost" console. This console will receive events
60 sent with CON_Events() */
61 void CON_Topmost(ConsoleInformation *console);
62 /*! Modify the prompt of the console */
63 void CON_SetPrompt(ConsoleInformation *console, char* newprompt);
64 /*! Set the key, that invokes a CON_Hide() after press. default is ESCAPE and you can always hide using
65 ESCAPE and the HideKey. compared against event->key.keysym.sym !! */
66 void CON_SetHideKey(ConsoleInformation *console, int key);
67 /*! Internal: executes the command typed in at the console (called if you press ENTER)*/
68 void CON_SetHideFunction(ConsoleInformation *console, void(*HideFunction)(void));
69 /*! Sets the callback function that is called after a console has been hidden */
70 void CON_Execute(ConsoleInformation *console, char* command);
71 /*! Sets the callback function that is called if a command was typed in. The function could look like this:
72 void my_command_handler(ConsoleInformation* console, char* command). @param console: the console the command
73 came from. @param command: the command string that was typed in. */
74 void CON_SetExecuteFunction(ConsoleInformation *console, void(*CmdFunction)(ConsoleInformation *console2, char* command));
75 /*! Sets the callback tabulator completion function. char* my_tabcompletion(char* command). If Tab is
76 pressed, the function gets called with the already typed in command. my_tabcompletion then checks if if can
77 complete the command or if it should display a list of all matching commands (with CON_Out()). Returns the
78 completed command or NULL if no completion was made. */
79 void CON_SetTabCompletion(ConsoleInformation *console, char*(*TabFunction)(char* command));
80 /*! Internal: Gets called when TAB was pressed */
81 void CON_TabCompletion(ConsoleInformation *console);
82 /*! Internal: makes newline (same as printf("\n") or CON_Out(console, "\n") ) */
83 void CON_NewLineConsole(ConsoleInformation *console);
84 /*! Internal: shift command history (the one you can switch with the up/down keys) */
85 void CON_NewLineCommand(ConsoleInformation *console);
86 /*! Internal: updates console after resize etc. */
87 void CON_UpdateConsole(ConsoleInformation *console);
90 /*! Internal: Default Execute callback */
91 void Default_CmdFunction(ConsoleInformation *console, char* command);
92 /*! Internal: Default TabCompletion callback */
93 char* Default_TabFunction(char* command);
94 /*! Internal: Default Hide callback */
95 void Default_HideFunction(void);
97 /*! Internal: draws the commandline the user is typing in to the screen. called by update? */
98 void DrawCommandLine();
100 /*! Internal: Gets called if you press the LEFT key (move cursor left) */
101 void Cursor_Left(ConsoleInformation *console);
102 /*! Internal: Gets called if you press the RIGHT key (move cursor right) */
103 void Cursor_Right(ConsoleInformation *console);
104 /*! Internal: Gets called if you press the HOME key (move cursor to the beginning
106 void Cursor_Home(ConsoleInformation *console);
107 /*! Internal: Gets called if you press the END key (move cursor to the end of the line*/
108 void Cursor_End(ConsoleInformation *console);
109 /*! Internal: Called if you press DELETE (deletes character under the cursor) */
110 void Cursor_Del(ConsoleInformation *console);
111 /*! Internal: Called if you press BACKSPACE (deletes character left of cursor) */
112 void Cursor_BSpace(ConsoleInformation *console);
113 /*! Internal: Called if you type in a character (add the char to the command) */
114 void Cursor_Add(ConsoleInformation *console, int event);
116 /*! Internal: Called if you press Ctrl-C (deletes the commandline) */
117 void Clear_Command(ConsoleInformation *console);
118 /*! Internal: Called if you press Ctrl-L (deletes the History) */
119 void Clear_History(ConsoleInformation *console);
121 /*! Internal: Called if you press UP key (switches through recent typed in commands */
122 void Command_Up(ConsoleInformation *console);
123 /*! Internal: Called if you press DOWN key (switches through recent typed in commands */
124 void Command_Down(ConsoleInformation *console);
127 /* Takes keys from the keyboard and inputs them to the console
128 If the event was not handled (i.e. WM events or unknown ctrl-shift
129 sequences) the function returns the event for further processing. */
130 int CON_Events(int event)
134 if(!CON_isVisible(Topmost))
137 if(event & KEY_CTRLED)
140 switch(event & ~KEY_CTRLED)
143 Cursor_Home(Topmost);
149 Clear_Command(Topmost);
152 Clear_History(Topmost);
153 CON_UpdateConsole(Topmost);
159 else if(event & KEY_ALTED)
161 //the console does not handle ALT combinations!
166 //first of all, check if the console hide key was pressed
167 if(event == Topmost->HideKey)
172 switch (event & 0xff)
178 if(event & KEY_SHIFTED)
180 Topmost->ConsoleScrollBack = Topmost->LineBuffer-1;
181 CON_UpdateConsole(Topmost);
183 Cursor_Home(Topmost);
187 if(event & KEY_SHIFTED)
189 Topmost->ConsoleScrollBack = 0;
190 CON_UpdateConsole(Topmost);
196 Topmost->ConsoleScrollBack += CON_LINE_SCROLL;
197 if(Topmost->ConsoleScrollBack > Topmost->LineBuffer-1)
198 Topmost->ConsoleScrollBack = Topmost->LineBuffer-1;
200 CON_UpdateConsole(Topmost);
203 Topmost->ConsoleScrollBack -= CON_LINE_SCROLL;
204 if(Topmost->ConsoleScrollBack < 0)
205 Topmost->ConsoleScrollBack = 0;
206 CON_UpdateConsole(Topmost);
212 Command_Down(Topmost);
215 Cursor_Left(Topmost);
218 Cursor_Right(Topmost);
221 Cursor_BSpace(Topmost);
227 Topmost->InsMode = 1-Topmost->InsMode;
230 CON_TabCompletion(Topmost);
233 if(strlen(Topmost->Command) > 0) {
234 CON_NewLineCommand(Topmost);
236 // copy the input into the past commands strings
237 strcpy(Topmost->CommandLines[0], Topmost->Command);
239 // display the command including the prompt
240 CON_Out(Topmost, "%s%s", Topmost->Prompt, Topmost->Command);
241 CON_UpdateConsole(Topmost);
243 CON_Execute(Topmost, Topmost->Command);
244 //printf("Command: %s\n", Topmost->Command);
246 Clear_Command(Topmost);
247 Topmost->CommandScrollBack = -1;
255 if (key_to_ascii(event) == 255)
258 Cursor_Add(Topmost, event);
260 Cursor_Add(Topmost, event);
269 /* CON_AlphaGL() -- sets the alpha channel of an SDL_Surface to the
270 * specified value. Preconditions: the surface in question is RGBA.
271 * 0 <= a <= 255, where 0 is transparent and 255 is opaque. */
272 void CON_AlphaGL(SDL_Surface *s, int alpha) {
277 SDL_PixelFormat *format;
278 static char errorPrinted = 0;
281 /* debugging assertions -- these slow you down, but hey, crashing sucks */
283 PRINT_ERROR("NULL Surface passed to CON_AlphaGL\n");
287 /* clamp alpha value to 0...255 */
288 if(alpha < SDL_ALPHA_TRANSPARENT)
289 val = SDL_ALPHA_TRANSPARENT;
290 else if(alpha > SDL_ALPHA_OPAQUE)
291 val = SDL_ALPHA_OPAQUE;
295 /* loop over alpha channels of each pixel, setting them appropriately. */
299 switch (format->BytesPerPixel) {
301 /* 16-bit surfaces don't seem to support alpha channels. */
304 PRINT_ERROR("16-bit SDL surfaces do not support alpha-blending under OpenGL.\n");
308 /* we can do this very quickly in 32-bit mode. 24-bit is more
309 * difficult. And since 24-bit mode is reall the same as 32-bit,
310 * so it usually ends up taking this route too. Win! Unroll loop
311 * and use pointer arithmetic for extra speed. */
312 int numpixels = h * (w << 2);
313 Uint8 *pix = (Uint8 *) (s->pixels);
314 Uint8 *last = pix + numpixels;
316 if((numpixels & 0x7) == 0)
317 for(pixel = pix + 3; pixel < last; pixel += 32)
318 *pixel = *(pixel + 4) = *(pixel + 8) = *(pixel + 12) = *(pixel + 16) = *(pixel + 20) = *(pixel + 24) = *(pixel + 28) = val;
320 for(pixel = pix + 3; pixel < last; pixel += 4)
325 /* we have no choice but to do this slowly. <sigh> */
326 for(y = 0; y < h; ++y)
327 for(x = 0; x < w; ++x) {
329 /* Lock the surface for direct access to the pixels */
330 if(SDL_MUSTLOCK(s) && SDL_LockSurface(s) < 0) {
331 PRINT_ERROR("Can't lock surface: ");
332 fprintf(stderr, "%s\n", SDL_GetError());
335 pixel = DT_GetPixel(s, x, y);
338 SDL_GetRGBA(pixel, format, &r, &g, &b, &a);
339 pixel = SDL_MapRGBA(format, r, g, b, val);
340 SDL_GetRGBA(pixel, format, &r, &g, &b, &a);
341 DT_PutPixel(s, x, y, pixel);
343 /* unlock surface again */
345 SDL_UnlockSurface(s);
353 /* Updates the console buffer */
354 void CON_UpdateConsole(ConsoleInformation *console) {
358 grs_canvas *canv_save;
364 /* Due to the Blits, the update is not very fast: So only update if it's worth it */
365 if(!CON_isVisible(console))
368 Screenlines = console->ConsoleSurface->cv_h / (CON_LINE_SPACE + console->ConsoleSurface->cv_font->ft_h);
370 canv_save = grd_curcanv;
371 gr_set_current_canvas(console->ConsoleSurface);
374 SDL_FillRect(console->ConsoleSurface, NULL, SDL_MapRGBA(console->ConsoleSurface->format, 0, 0, 0, console->ConsoleAlpha));
380 if(console->OutputScreen->flags & SDL_OPENGLBLIT)
381 SDL_SetAlpha(console->ConsoleSurface, 0, SDL_ALPHA_OPAQUE);
384 /* draw the background image if there is one */
385 if(console->BackgroundImage)
386 gr_bitmap(0, 0, console->BackgroundImage);
388 /* Draw the text from the back buffers, calculate in the scrollback from the user
389 * this is a normal SDL software-mode blit, so we need to temporarily set the ColorKey
390 * for the font, and then clear it when we're done.
393 if((console->OutputScreen->flags & SDL_OPENGLBLIT) && (console->OutputScreen->format->BytesPerPixel > 2)) {
394 Uint32 *pix = (Uint32 *) (CurrentFont->FontSurface->pixels);
395 SDL_SetColorKey(CurrentFont->FontSurface, SDL_SRCCOLORKEY, *pix);
399 //now draw text from last but second line to top
400 for(loop = 0; loop < Screenlines-1 && loop < console->LineBuffer - console->ConsoleScrollBack; loop++) {
401 if(console->ConsoleScrollBack != 0 && loop == 0)
402 for(loop2 = 0; loop2 < (console->VChars / 5) + 1; loop2++)
404 orig_color = FG_COLOR;
405 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);
406 FG_COLOR = orig_color;
410 orig_color = FG_COLOR;
411 gr_string(CON_CHAR_BORDER, (Screenlines - loop - 2) * (CON_LINE_SPACE + console->ConsoleSurface->cv_font->ft_h), console->ConsoleLines[console->ConsoleScrollBack + loop]);
412 FG_COLOR = orig_color;
416 gr_set_current_canvas(canv_save);
419 if(console->OutputScreen->flags & SDL_OPENGLBLIT)
420 SDL_SetColorKey(CurrentFont->FontSurface, 0, 0);
424 void CON_UpdateOffset(ConsoleInformation* console) {
428 switch(console->Visible) {
430 console->RaiseOffset -= CON_OPENCLOSE_SPEED;
431 if(console->RaiseOffset <= 0) {
432 console->RaiseOffset = 0;
433 console->Visible = CON_CLOSED;
437 console->RaiseOffset += CON_OPENCLOSE_SPEED;
438 if(console->RaiseOffset >= console->ConsoleSurface->cv_h) {
439 console->RaiseOffset = console->ConsoleSurface->cv_h;
440 console->Visible = CON_OPEN;
449 /* Draws the console buffer to the screen if the console is "visible" */
450 void CON_DrawConsole(ConsoleInformation *console) {
451 grs_canvas *canv_save;
457 /* only draw if console is visible: here this means, that the console is not CON_CLOSED */
458 if(console->Visible == CON_CLOSED)
461 /* Update the scrolling offset */
462 CON_UpdateOffset(console);
464 /* Update the command line since it has a blinking cursor */
468 /* before drawing, make sure the alpha channel of the console surface is set
469 * properly. (sigh) I wish we didn't have to do this every frame... */
470 if(console->OutputScreen->flags & SDL_OPENGLBLIT)
471 CON_AlphaGL(console->ConsoleSurface, console->ConsoleAlpha);
474 canv_save = grd_curcanv;
475 gr_set_current_canvas(&console->OutputScreen->sc_canvas);
477 clip = gr_create_sub_bitmap(&console->ConsoleSurface->cv_bitmap, 0, console->ConsoleSurface->cv_h - console->RaiseOffset, console->ConsoleSurface->cv_w, console->RaiseOffset);
479 gr_bitmap(console->DispX, console->DispY, clip);
480 gr_free_sub_bitmap(clip);
483 if(console->OutputScreen->flags & SDL_OPENGLBLIT)
484 SDL_UpdateRects(console->OutputScreen, 1, &DestRect);
487 gr_set_current_canvas(canv_save);
491 /* Initializes the console */
492 ConsoleInformation *CON_Init(grs_font *Font, grs_screen *DisplayScreen, int lines, int x, int y, int w, int h)
495 ConsoleInformation *newinfo;
498 /* Create a new console struct and init it. */
499 if((newinfo = (ConsoleInformation *) d_malloc(sizeof(ConsoleInformation))) == NULL) {
500 //PRINT_ERROR("Could not allocate the space for a new console info struct.\n");
503 newinfo->Visible = CON_CLOSED;
504 newinfo->RaiseOffset = 0;
505 newinfo->ConsoleLines = NULL;
506 newinfo->CommandLines = NULL;
507 newinfo->TotalConsoleLines = 0;
508 newinfo->ConsoleScrollBack = 0;
509 newinfo->TotalCommands = 0;
510 newinfo->BackgroundImage = NULL;
512 newinfo->ConsoleAlpha = SDL_ALPHA_OPAQUE;
515 newinfo->InsMode = 1;
516 newinfo->CursorPos = 0;
517 newinfo->CommandScrollBack = 0;
518 newinfo->OutputScreen = DisplayScreen;
519 newinfo->Prompt = CON_DEFAULT_PROMPT;
520 newinfo->HideKey = CON_DEFAULT_HIDEKEY;
522 CON_SetExecuteFunction(newinfo, Default_CmdFunction);
523 CON_SetTabCompletion(newinfo, Default_TabFunction);
524 CON_SetHideFunction(newinfo, Default_HideFunction);
526 /* make sure that the size of the console is valid */
527 if(w > newinfo->OutputScreen->sc_w || w < Font->ft_w * 32)
528 w = newinfo->OutputScreen->sc_w;
529 if(h > newinfo->OutputScreen->sc_h || h < Font->ft_h)
530 h = newinfo->OutputScreen->sc_h;
531 if(x < 0 || x > newinfo->OutputScreen->sc_w - w)
535 if(y < 0 || y > newinfo->OutputScreen->sc_h - h)
540 /* load the console surface */
541 newinfo->ConsoleSurface = gr_create_canvas(w, h);
543 /* Load the consoles font */
545 grs_canvas *canv_save;
547 canv_save = grd_curcanv;
548 gr_set_current_canvas(newinfo->ConsoleSurface);
549 gr_set_curfont(Font);
550 gr_set_fontcolor(gr_getcolor(63,63,63), -1);
551 gr_set_current_canvas(canv_save);
555 /* Load the dirty rectangle for user input */
556 newinfo->InputBackground = gr_create_bitmap(w, newinfo->ConsoleSurface->cv_font->ft_h);
558 SDL_FillRect(newinfo->InputBackground, NULL, SDL_MapRGBA(newinfo->ConsoleSurface->format, 0, 0, 0, SDL_ALPHA_OPAQUE));
561 /* calculate the number of visible characters in the command line */
562 newinfo->VChars = (w - CON_CHAR_BORDER) / newinfo->ConsoleSurface->cv_font->ft_w;
563 if(newinfo->VChars > CON_CHARS_PER_LINE)
564 newinfo->VChars = CON_CHARS_PER_LINE;
566 /* We would like to have a minumum # of lines to guarentee we don't create a memory error */
567 if(h / (CON_LINE_SPACE + newinfo->ConsoleSurface->cv_font->ft_h) > lines)
568 newinfo->LineBuffer = h / (CON_LINE_SPACE + newinfo->ConsoleSurface->cv_font->ft_h);
570 newinfo->LineBuffer = lines;
573 newinfo->ConsoleLines = (char **)d_malloc(sizeof(char *) * newinfo->LineBuffer);
574 newinfo->CommandLines = (char **)d_malloc(sizeof(char *) * newinfo->LineBuffer);
575 for(loop = 0; loop <= newinfo->LineBuffer - 1; loop++) {
576 newinfo->ConsoleLines[loop] = (char *)d_calloc(CON_CHARS_PER_LINE, sizeof(char));
577 newinfo->CommandLines[loop] = (char *)d_calloc(CON_CHARS_PER_LINE, sizeof(char));
579 memset(newinfo->Command, 0, CON_CHARS_PER_LINE);
580 memset(newinfo->LCommand, 0, CON_CHARS_PER_LINE);
581 memset(newinfo->RCommand, 0, CON_CHARS_PER_LINE);
582 memset(newinfo->VCommand, 0, CON_CHARS_PER_LINE);
585 CON_Out(newinfo, "Console initialised.");
586 CON_NewLineConsole(newinfo);
587 //CON_ListCommands(newinfo);
592 /* Makes the console visible */
593 void CON_Show(ConsoleInformation *console) {
595 console->Visible = CON_OPENING;
596 CON_UpdateConsole(console);
600 /* Hides the console (make it invisible) */
601 void CON_Hide(ConsoleInformation *console) {
603 console->Visible = CON_CLOSING;
604 console->HideFunction();
607 /* tells wether the console is visible or not */
608 int CON_isVisible(ConsoleInformation *console) {
611 return((console->Visible == CON_OPEN) || (console->Visible == CON_OPENING));
614 /* Frees all the memory loaded by the console */
615 void CON_Destroy(ConsoleInformation *console) {
619 /* Frees all the memory loaded by the console */
620 void CON_Free(ConsoleInformation *console) {
626 //CON_DestroyCommands();
627 for(i = 0; i <= console->LineBuffer - 1; i++) {
628 d_free(console->ConsoleLines[i]);
629 d_free(console->CommandLines[i]);
631 d_free(console->ConsoleLines);
632 d_free(console->CommandLines);
634 console->ConsoleLines = NULL;
635 console->CommandLines = NULL;
637 gr_free_canvas(console->ConsoleSurface);
638 console->ConsoleSurface = NULL;
640 if (console->BackgroundImage)
641 gr_free_bitmap(console->BackgroundImage);
642 console->BackgroundImage = NULL;
644 gr_free_bitmap(console->InputBackground);
645 console->InputBackground = NULL;
651 /* Increments the console lines */
652 void CON_NewLineConsole(ConsoleInformation *console) {
659 temp = console->ConsoleLines[console->LineBuffer - 1];
661 for(loop = console->LineBuffer - 1; loop > 0; loop--)
662 console->ConsoleLines[loop] = console->ConsoleLines[loop - 1];
664 console->ConsoleLines[0] = temp;
666 memset(console->ConsoleLines[0], 0, CON_CHARS_PER_LINE);
667 if(console->TotalConsoleLines < console->LineBuffer - 1)
668 console->TotalConsoleLines++;
670 //Now adjust the ConsoleScrollBack
671 //dont scroll if not at bottom
672 if(console->ConsoleScrollBack != 0)
673 console->ConsoleScrollBack++;
675 if(console->ConsoleScrollBack > console->LineBuffer-1)
676 console->ConsoleScrollBack = console->LineBuffer-1;
681 /* Increments the command lines */
682 void CON_NewLineCommand(ConsoleInformation *console) {
689 temp = console->CommandLines[console->LineBuffer - 1];
692 for(loop = console->LineBuffer - 1; loop > 0; loop--)
693 console->CommandLines[loop] = console->CommandLines[loop - 1];
695 console->CommandLines[0] = temp;
697 memset(console->CommandLines[0], 0, CON_CHARS_PER_LINE);
698 if(console->TotalCommands < console->LineBuffer - 1)
699 console->TotalCommands++;
702 /* Draws the command line the user is typing in to the screen */
703 /* completely rewritten by C.Wacha */
704 void DrawCommandLine() {
708 grs_font* CurrentFont;
710 static unsigned int LastBlinkTime = 0; /* Last time the consoles cursor blinked */
711 static int LastCursorPos = 0; // Last Cursor Position
712 static int Blink = 0; /* Is the cursor currently blinking */
713 grs_canvas *canv_save;
719 commandbuffer = Topmost->VChars - strlen(Topmost->Prompt)-1; // -1 to make cursor visible
722 CurrentFont = Topmost->ConsoleSurface->cv_font;
725 //Concatenate the left and right side to command
726 strcpy(Topmost->Command, Topmost->LCommand);
727 strncat(Topmost->Command, Topmost->RCommand, strlen(Topmost->RCommand));
729 //calculate display offset from current cursor position
730 if(Topmost->Offset < Topmost->CursorPos - commandbuffer)
731 Topmost->Offset = Topmost->CursorPos - commandbuffer;
732 if(Topmost->Offset > Topmost->CursorPos)
733 Topmost->Offset = Topmost->CursorPos;
735 //first add prompt to visible part
736 strcpy(Topmost->VCommand, Topmost->Prompt);
738 //then add the visible part of the command
739 strncat(Topmost->VCommand, &Topmost->Command[Topmost->Offset], strlen(&Topmost->Command[Topmost->Offset]));
741 //now display the result
744 //once again we're drawing text, so in OpenGL context we need to temporarily set up
745 //software-mode transparency.
746 if(Topmost->OutputScreen->flags & SDL_OPENGLBLIT) {
747 Uint32 *pix = (Uint32 *) (CurrentFont->FontSurface->pixels);
748 SDL_SetColorKey(CurrentFont->FontSurface, SDL_SRCCOLORKEY, *pix);
752 canv_save = grd_curcanv;
753 gr_set_current_canvas(Topmost->ConsoleSurface);
755 //first of all restore InputBackground
756 gr_bitmap(0, Topmost->ConsoleSurface->cv_h - Topmost->ConsoleSurface->cv_font->ft_h, Topmost->InputBackground);
759 orig_color = FG_COLOR;
760 gr_string(CON_CHAR_BORDER, Topmost->ConsoleSurface->cv_h - Topmost->ConsoleSurface->cv_font->ft_h, Topmost->VCommand);
761 FG_COLOR = orig_color;
763 //at last add the cursor
764 //check if the blink period is over
765 if(get_msecs() > LastBlinkTime) {
766 LastBlinkTime = get_msecs() + CON_BLINK_RATE;
773 //check if cursor has moved - if yes display cursor anyway
774 if(Topmost->CursorPos != LastCursorPos) {
775 LastCursorPos = Topmost->CursorPos;
776 LastBlinkTime = get_msecs() + CON_BLINK_RATE;
781 int prompt_width, cmd_width, h, w;
783 gr_get_string_size(Topmost->Prompt, &prompt_width, &h, &w);
784 gr_get_string_size(Topmost->LCommand + Topmost->Offset, &cmd_width, &h, &w);
785 x = CON_CHAR_BORDER + prompt_width + cmd_width;
786 orig_color = FG_COLOR;
788 gr_string(x, Topmost->ConsoleSurface->cv_h - Topmost->ConsoleSurface->cv_font->ft_h, CON_INS_CURSOR);
790 gr_string(x, Topmost->ConsoleSurface->cv_h - Topmost->ConsoleSurface->cv_font->ft_h, CON_OVR_CURSOR);
791 FG_COLOR = orig_color;
794 gr_set_current_canvas(canv_save);
798 if(Topmost->OutputScreen->flags & SDL_OPENGLBLIT) {
799 SDL_SetColorKey(CurrentFont->FontSurface, 0, 0);
805 # define vsnprintf _vsnprintf
808 /* Outputs text to the console (in game), up to CON_CHARS_PER_LINE chars can be entered */
809 void CON_Out(ConsoleInformation *console, const char *str, ...) {
811 //keep some space free for stuff like CON_Out(console, "blablabla %s", console->Command);
812 char temp[CON_CHARS_PER_LINE + 128];
818 va_start(marker, str);
819 vsnprintf(temp, CON_CHARS_PER_LINE + 127, str, marker);
824 //temp now contains the complete string we want to output
825 // the only problem is that temp is maybe longer than the console
826 // width so we have to cut it into several pieces
828 if(console->ConsoleLines) {
829 while(strlen(ptemp) > console->VChars) {
830 CON_NewLineConsole(console);
831 strncpy(console->ConsoleLines[0], ptemp, console->VChars);
832 console->ConsoleLines[0][console->VChars] = '\0';
833 ptemp = &ptemp[console->VChars];
835 CON_NewLineConsole(console);
836 strncpy(console->ConsoleLines[0], ptemp, console->VChars);
837 console->ConsoleLines[0][console->VChars] = '\0';
838 CON_UpdateConsole(console);
841 /* And print to stdout */
842 //printf("%s\n", temp);
847 /* Sets the alpha level of the console, 0 turns off alpha blending */
848 void CON_Alpha(ConsoleInformation *console, unsigned char alpha) {
852 /* store alpha as state! */
853 console->ConsoleAlpha = alpha;
855 if((console->OutputScreen->flags & SDL_OPENGLBLIT) == 0) {
857 SDL_SetAlpha(console->ConsoleSurface, 0, alpha);
859 SDL_SetAlpha(console->ConsoleSurface, SDL_SRCALPHA, alpha);
862 // CON_UpdateConsole(console);
867 /* Adds background image to the console, scaled to size of console*/
868 int CON_Background(ConsoleInformation *console, grs_bitmap *image)
873 /* Free the background from the console */
875 if (console->BackgroundImage)
876 gr_free_bitmap(console->BackgroundImage);
877 console->BackgroundImage = NULL;
879 SDL_FillRect(console->InputBackground, NULL, SDL_MapRGBA(console->ConsoleSurface->format, 0, 0, 0, SDL_ALPHA_OPAQUE));
884 /* Load a new background */
885 if (console->BackgroundImage)
886 gr_free_bitmap(console->BackgroundImage);
887 console->BackgroundImage = gr_create_bitmap(console->ConsoleSurface->cv_w, console->ConsoleSurface->cv_h);
888 gr_bitmap_scale_to(image, console->BackgroundImage);
891 SDL_FillRect(console->InputBackground, NULL, SDL_MapRGBA(console->ConsoleSurface->format, 0, 0, 0, SDL_ALPHA_OPAQUE));
893 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);
898 /* Sets font info for the console */
899 void CON_Font(ConsoleInformation *console, grs_font *font, int fg, int bg)
901 grs_canvas *canv_save;
903 canv_save = grd_curcanv;
904 gr_set_current_canvas(console->ConsoleSurface);
905 gr_set_curfont(font);
906 gr_set_fontcolor(fg, bg);
907 gr_set_current_canvas(canv_save);
910 /* takes a new x and y of the top left of the console window */
911 void CON_Position(ConsoleInformation *console, int x, int y) {
915 if(x < 0 || x > console->OutputScreen->sc_w - console->ConsoleSurface->cv_w)
920 if(y < 0 || y > console->OutputScreen->sc_h - console->ConsoleSurface->cv_h)
926 void gr_init_bitmap_alloc( grs_bitmap *bm, int mode, int x, int y, int w, int h, int bytesperline);
927 /* resizes the console, has to reset alot of stuff
928 * returns 1 on error */
929 int CON_Resize(ConsoleInformation *console, int x, int y, int w, int h)
934 /* make sure that the size of the console is valid */
935 if(w > console->OutputScreen->sc_w || w < console->ConsoleSurface->cv_font->ft_w * 32)
936 w = console->OutputScreen->sc_w;
937 if(h > console->OutputScreen->sc_h || h < console->ConsoleSurface->cv_font->ft_h)
938 h = console->OutputScreen->sc_h;
939 if(x < 0 || x > console->OutputScreen->sc_w - w)
943 if(y < 0 || y > console->OutputScreen->sc_h - h)
948 /* resize console surface */
949 gr_free_bitmap_data(&console->ConsoleSurface->cv_bitmap);
950 gr_init_bitmap_alloc(&console->ConsoleSurface->cv_bitmap, BM_LINEAR, 0, 0, w, h, w);
952 /* Load the dirty rectangle for user input */
953 gr_free_bitmap(console->InputBackground);
954 console->InputBackground = gr_create_bitmap(w, console->ConsoleSurface->cv_font->ft_h);
956 /* Now reset some stuff dependent on the previous size */
957 console->ConsoleScrollBack = 0;
959 /* Reload the background image (for the input text area) in the console */
960 if(console->BackgroundImage) {
962 SDL_FillRect(console->InputBackground, NULL, SDL_MapRGBA(console->ConsoleSurface->format, 0, 0, 0, SDL_ALPHA_OPAQUE));
964 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);
968 /* restore the alpha level */
969 CON_Alpha(console, console->ConsoleAlpha);
974 /* Transfers the console to another screen surface, and adjusts size */
975 int CON_Transfer(ConsoleInformation *console, grs_screen *new_outputscreen, int x, int y, int w, int h)
980 console->OutputScreen = new_outputscreen;
982 return(CON_Resize(console, x, y, w, h));
985 /* Sets the topmost console for input */
986 void CON_Topmost(ConsoleInformation *console) {
987 grs_canvas *canv_save;
993 // Make sure the blinking cursor is gone
995 canv_save = grd_curcanv;
996 gr_set_current_canvas(Topmost->ConsoleSurface);
998 gr_bitmap(0, Topmost->ConsoleSurface->cv_h - Topmost->ConsoleSurface->cv_font->ft_h, Topmost->InputBackground);
999 orig_color = FG_COLOR;
1000 gr_string(CON_CHAR_BORDER, Topmost->ConsoleSurface->cv_h - Topmost->ConsoleSurface->cv_font->ft_h, Topmost->VCommand);
1001 FG_COLOR = orig_color;
1003 gr_set_current_canvas(canv_save);
1008 /* Sets the Prompt for console */
1009 void CON_SetPrompt(ConsoleInformation *console, char* newprompt) {
1013 //check length so we can still see at least 1 char :-)
1014 if(strlen(newprompt) < console->VChars)
1015 console->Prompt = d_strdup(newprompt);
1017 CON_Out(console, "prompt too long. (max. %i chars)", console->VChars - 1);
1020 /* Sets the key that deactivates (hides) the console. */
1021 void CON_SetHideKey(ConsoleInformation *console, int key) {
1023 console->HideKey = key;
1026 void CON_SetHideFunction(ConsoleInformation *console, void(*HideFunction)(void)) {
1028 console->HideFunction = HideFunction;
1031 void Default_HideFunction(void) {
1034 /* Executes the command entered */
1035 void CON_Execute(ConsoleInformation *console, char* command) {
1037 console->CmdFunction(console, command);
1040 void CON_SetExecuteFunction(ConsoleInformation *console, void(*CmdFunction)(ConsoleInformation *console2, char* command)) {
1042 console->CmdFunction = CmdFunction;
1045 void Default_CmdFunction(ConsoleInformation *console, char* command) {
1046 CON_Out(console, " No CommandFunction registered");
1047 CON_Out(console, " use 'CON_SetExecuteFunction' to register one");
1048 CON_Out(console, " ");
1049 CON_Out(console, "Unknown Command \"%s\"", command);
1052 void CON_SetTabCompletion(ConsoleInformation *console, char*(*TabFunction)(char* command)) {
1054 console->TabFunction = TabFunction;
1057 void CON_TabCompletion(ConsoleInformation *console) {
1064 command = d_strdup(console->LCommand);
1065 command = console->TabFunction(command);
1068 return; //no tab completion took place so return silently
1070 j = strlen(command);
1071 if(j > CON_CHARS_PER_LINE - 2)
1072 j = CON_CHARS_PER_LINE-1;
1074 memset(console->LCommand, 0, CON_CHARS_PER_LINE);
1075 console->CursorPos = 0;
1077 for(i = 0; i < j; i++) {
1078 console->CursorPos++;
1079 console->LCommand[i] = command[i];
1081 //add a trailing space
1082 console->CursorPos++;
1083 console->LCommand[j] = ' ';
1084 console->LCommand[j+1] = '\0';
1087 char* Default_TabFunction(char* command) {
1088 CON_Out(Topmost, " No TabFunction registered");
1089 CON_Out(Topmost, " use 'CON_SetTabCompletion' to register one");
1090 CON_Out(Topmost, " ");
1094 void Cursor_Left(ConsoleInformation *console) {
1095 char temp[CON_CHARS_PER_LINE];
1097 if(Topmost->CursorPos > 0) {
1098 Topmost->CursorPos--;
1099 strcpy(temp, Topmost->RCommand);
1100 strcpy(Topmost->RCommand, &Topmost->LCommand[strlen(Topmost->LCommand)-1]);
1101 strcat(Topmost->RCommand, temp);
1102 Topmost->LCommand[strlen(Topmost->LCommand)-1] = '\0';
1103 //CON_Out(Topmost, "L:%s, R:%s", Topmost->LCommand, Topmost->RCommand);
1107 void Cursor_Right(ConsoleInformation *console) {
1108 char temp[CON_CHARS_PER_LINE];
1110 if(Topmost->CursorPos < strlen(Topmost->Command)) {
1111 Topmost->CursorPos++;
1112 strncat(Topmost->LCommand, Topmost->RCommand, 1);
1113 strcpy(temp, Topmost->RCommand);
1114 strcpy(Topmost->RCommand, &temp[1]);
1115 //CON_Out(Topmost, "L:%s, R:%s", Topmost->LCommand, Topmost->RCommand);
1119 void Cursor_Home(ConsoleInformation *console) {
1120 char temp[CON_CHARS_PER_LINE];
1122 Topmost->CursorPos = 0;
1123 strcpy(temp, Topmost->RCommand);
1124 strcpy(Topmost->RCommand, Topmost->LCommand);
1125 strncat(Topmost->RCommand, temp, strlen(temp));
1126 memset(Topmost->LCommand, 0, CON_CHARS_PER_LINE);
1129 void Cursor_End(ConsoleInformation *console) {
1130 Topmost->CursorPos = strlen(Topmost->Command);
1131 strncat(Topmost->LCommand, Topmost->RCommand, strlen(Topmost->RCommand));
1132 memset(Topmost->RCommand, 0, CON_CHARS_PER_LINE);
1135 void Cursor_Del(ConsoleInformation *console) {
1136 char temp[CON_CHARS_PER_LINE];
1138 if(strlen(Topmost->RCommand) > 0) {
1139 strcpy(temp, Topmost->RCommand);
1140 strcpy(Topmost->RCommand, &temp[1]);
1144 void Cursor_BSpace(ConsoleInformation *console) {
1145 if(Topmost->CursorPos > 0) {
1146 Topmost->CursorPos--;
1148 if(Topmost->Offset < 0)
1149 Topmost->Offset = 0;
1150 Topmost->LCommand[strlen(Topmost->LCommand)-1] = '\0';
1154 void Cursor_Add(ConsoleInformation *console, int event)
1156 if(strlen(Topmost->Command) < CON_CHARS_PER_LINE - 1)
1158 Topmost->CursorPos++;
1159 Topmost->LCommand[strlen(Topmost->LCommand)] = key_to_ascii(event);
1160 Topmost->LCommand[strlen(Topmost->LCommand)] = '\0';
1164 void Clear_Command(ConsoleInformation *console) {
1165 Topmost->CursorPos = 0;
1166 memset(Topmost->VCommand, 0, CON_CHARS_PER_LINE);
1167 memset(Topmost->Command, 0, CON_CHARS_PER_LINE);
1168 memset(Topmost->LCommand, 0, CON_CHARS_PER_LINE);
1169 memset(Topmost->RCommand, 0, CON_CHARS_PER_LINE);
1172 void Clear_History(ConsoleInformation *console) {
1175 for(loop = 0; loop <= console->LineBuffer - 1; loop++)
1176 memset(console->ConsoleLines[loop], 0, CON_CHARS_PER_LINE);
1179 void Command_Up(ConsoleInformation *console) {
1180 if(console->CommandScrollBack < console->TotalCommands - 1) {
1181 /* move back a line in the command strings and copy the command to the current input string */
1182 console->CommandScrollBack++;
1183 memset(console->RCommand, 0, CON_CHARS_PER_LINE);
1184 console->Offset = 0;
1185 strcpy(console->LCommand, console->CommandLines[console->CommandScrollBack]);
1186 console->CursorPos = strlen(console->CommandLines[console->CommandScrollBack]);
1187 CON_UpdateConsole(console);
1191 void Command_Down(ConsoleInformation *console) {
1192 if(console->CommandScrollBack > -1) {
1193 /* move forward a line in the command strings and copy the command to the current input string */
1194 console->CommandScrollBack--;
1195 memset(console->RCommand, 0, CON_CHARS_PER_LINE);
1196 memset(console->LCommand, 0, CON_CHARS_PER_LINE);
1197 console->Offset = 0;
1198 if(console->CommandScrollBack > -1)
1199 strcpy(console->LCommand, console->CommandLines[console->CommandScrollBack]);
1200 console->CursorPos = strlen(console->LCommand);
1201 CON_UpdateConsole(console);
1214 #include "pstypes.h"
1217 #include "console.h"
1221 #include "gamefont.h"
1226 int text_console_enabled = 1;
1229 #define text_console_enabled (!isvga())
1232 int Console_open = 0;
1234 /* Console specific cvars */
1235 /* How discriminating we are about which messages are displayed */
1236 cvar_t con_threshold = {"con_threshold", "0",};
1238 /* Private console stuff */
1239 #define CON_NUM_LINES 40
1241 #define CON_LINE_LEN 40
1242 static char con_display[40][40];
1243 static int con_line; /* Current display line */
1247 static int con_initialized;
1249 ConsoleInformation *Console;
1251 void con_parse(ConsoleInformation *console, char *command);
1255 /* Free the console */
1258 if (con_initialized)
1260 con_initialized = 0;
1265 /* Initialise the console */
1268 grs_screen fake_screen;
1271 fake_screen.sc_w = 320;
1272 fake_screen.sc_h = 200;
1276 Console = CON_Init(&fake_font, &fake_screen, CON_NUM_LINES, 0, 0, 320, 200);
1278 CON_SetExecuteFunction(Console, con_parse);
1279 CON_SetHideFunction(Console, con_hide);
1284 /* Initialise the cvars */
1285 cvar_registervariable (&con_threshold);
1287 con_initialized = 1;
1294 #define CON_BG_HIRES (cfexist("scoresb.pcx")?"scoresb.pcx":"scores.pcx")
1295 #define CON_BG_LORES (cfexist("scores.pcx")?"scores.pcx":"scoresb.pcx") // Mac datafiles only have scoresb.pcx
1296 #define CON_BG ((SWIDTH>=640)?CON_BG_HIRES:CON_BG_LORES)
1298 void con_background(char *filename)
1304 gr_init_bitmap_data(&bmp);
1305 pcx_error = pcx_read_bitmap(filename, &bmp, BM_LINEAR, pal);
1306 Assert(pcx_error == PCX_ERROR_NONE);
1307 gr_remap_bitmap_good(&bmp, pal, -1, -1);
1308 CON_Background(Console, &bmp);
1309 gr_free_bitmap_data(&bmp);
1313 void con_init_gfx(void)
1315 CON_Font(Console, SMALL_FONT, gr_getcolor(63, 63, 63), -1);
1316 CON_Transfer(Console, grd_curscreen, 0, 0, SWIDTH, SHEIGHT / 2);
1318 con_background(CON_BG);
1323 void con_resize(void)
1326 CON_Font(Console, SMALL_FONT, gr_getcolor(63, 63, 63), -1);
1327 CON_Resize(Console, 0, 0, SWIDTH, SHEIGHT / 2);
1328 con_background(CON_BG);
1332 /* Print a message to the console */
1333 void con_printf(int priority, char *fmt, ...)
1338 if (priority <= ((int)con_threshold.value))
1340 va_start (arglist, fmt);
1341 vsprintf (buffer, fmt, arglist);
1345 if (con_initialized)
1346 CON_Out(Console, buffer);
1349 /* for (i=0; i<l; i+=CON_LINE_LEN,con_line++)
1351 memcpy(con_display, &buffer[i], min(80, l-i));
1354 if (text_console_enabled)
1356 /* Produce a sanitised version and send it to the standard output */
1375 printf("%s", buffer);
1380 /* Check for new console input. If it's there, use it */
1381 void con_update(void)
1384 char buffer[CMD_MAX_LENGTH], *t;
1386 /* Check for new input */
1387 t = fgets(buffer, sizeof(buffer), stdin);
1388 if (t == NULL) return;
1396 int con_events(int key)
1399 return CON_Events(key);
1406 /* Draw the console */
1410 CON_DrawConsole(Console);
1413 char buffer[CON_LINE_LEN+1];
1415 for (i = con_line, j=0; j < 20; i = (i+1) % CON_NUM_LINES, j++)
1417 memcpy(buffer, con_display[i], CON_LINE_LEN);
1418 buffer[CON_LINE_LEN] = 0;
1419 gr_string(1,j*10,buffer);
1425 /* Show the console */
1431 CON_Topmost(Console);
1441 void con_parse(ConsoleInformation *console, char *command)