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