]> icculus.org git repositories - btb/d2x.git/blob - main/console.c
use the orientation parameter of g3_draw_bitmap
[btb/d2x.git] / main / console.c
1 /*
2  *  Code for controlling the console
3  *  Based on an early 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 #ifndef _WIN32_WCE
22 #include <fcntl.h>
23 #endif
24 #include <ctype.h>
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28
29 #include "inferno.h"
30 #include "u_mem.h"
31 #include "gr.h"
32 #include "key.h"
33 #include "timer.h"
34 #include "pstypes.h"
35 #include "dxxerror.h"
36 #include "cfile.h"
37
38
39 #ifndef __MSDOS__
40 int text_console_enabled = 1;
41 #else
42 int isvga();
43 #define text_console_enabled (!isvga())
44 #endif
45
46
47 /* How discriminating we are about which messages are displayed */
48 cvar_t con_threshold = { "con_threshold", "0", CVAR_NONE };
49
50 #define CON_BG_HIRES (cfexist("scoresb.pcx")?"scoresb.pcx":"scores.pcx")
51 #define CON_BG_LORES (cfexist("scores.pcx")?"scores.pcx":"scoresb.pcx") // Mac datafiles only have scoresb.pcx
52 #define CON_BG ((SWIDTH>=640)?CON_BG_HIRES:CON_BG_LORES)
53 #define CON_FONT SMALL_FONT
54 #define CON_DEFAULT_COLOR gr_find_closest_color(29, 29, 47)
55
56 #define CON_NUM_LINES           128
57 // Cut the buffer line if it becomes longer than this
58 #define CON_CHARS_PER_LINE      128
59 // Border in pixels from the most left to the first letter
60 #define CON_CHAR_BORDER         4
61 // Spacing in pixels between lines
62 #define CON_LINE_SPACE          1
63 // Scroll this many lines at a time (when pressing PGUP or PGDOWN)
64 #define CON_LINE_SCROLL         2
65 // Indicator showing that you scrolled up the history
66 #define CON_SCROLL_INDICATOR    "^"
67 // Defines the opening/closing speed
68 #define CON_OPENCLOSE_SPEED 50
69
70
71 /* The console's data */
72 static int Visible;             // Enum that tells which visible state we are in CON_HIDE, CON_SHOW, CON_RAISE, CON_LOWER
73 static int RaiseOffset;         // Offset used when scrolling in the console
74 static char **ConsoleLines;     // List of all the past lines
75 static int TotalConsoleLines;   // Total number of lines in the console
76 static int ConsoleScrollBack;   // How much the user scrolled back in the console
77 static int LineBuffer;          // The number of visible lines in the console (autocalculated)
78 static int VChars;              // The number of visible characters in one console line (autocalculated)
79 static grs_canvas *ConsoleSurface;  // Canvas of the console
80 static grs_bitmap *BackgroundImage; // Background image for the console
81 static grs_bitmap *InputBackground; // Dirty rectangle to draw over behind the users background
82
83 /* console is ready to be written to */
84 static int con_initialized;
85
86 /* text foreground color */
87 int CON_color;
88
89
90 /* Internals */
91 static void con_update_offset(void);
92 /* Frees all the memory loaded by the console */
93 static void con_free(void);
94 static int con_background(grs_bitmap *image);
95 /* Sets font info for the console */
96 static void con_font(grs_font *font);
97 /* makes newline (same as printf("\n") or CON_Out("\n") ) */
98 static void con_newline(void);
99 /* updates console after resize etc. */
100 static void con_update(void);
101 /* Called if you press Ctrl-L (deletes the History) */
102 static void con_clear(void);
103
104
105 /* Takes keys from the keyboard and inputs them to the console
106  * If the event was not handled (i.e. WM events or unknown ctrl-shift
107  * sequences) the function returns the event for further processing. */
108 int con_key_handler(int key)
109 {
110         unsigned char character = key_to_ascii(key);
111
112         if (!con_is_visible())
113                 return key;
114
115         switch (key) {
116                 case KEY_LAPOSTRO:
117                 case KEY_ESC:
118                 case KEY_SHIFTED + KEY_ESC:
119                         con_hide();
120                         break;
121                 case KEY_CTRLED + KEY_L:
122                         con_clear();
123                         con_update();
124                         break;
125                 case KEY_SHIFTED + KEY_HOME:
126                         ConsoleScrollBack = LineBuffer-1;
127                         con_update();
128                         break;
129                 case KEY_SHIFTED + KEY_END:
130                         ConsoleScrollBack = 0;
131                         con_update();
132                         break;
133                 case KEY_PAGEUP:
134                         ConsoleScrollBack += CON_LINE_SCROLL;
135                         if(ConsoleScrollBack > LineBuffer-1)
136                                 ConsoleScrollBack = LineBuffer-1;
137                         con_update();
138                         break;
139                 case KEY_PAGEDOWN:
140                         ConsoleScrollBack -= CON_LINE_SCROLL;
141                         if(ConsoleScrollBack < 0)
142                                 ConsoleScrollBack = 0;
143                         con_update();
144                         break;
145                 case KEY_CTRLED + KEY_A:
146                 case KEY_HOME:              cli_cursor_home();      break;
147                 case KEY_END:
148                 case KEY_CTRLED + KEY_E:    cli_cursor_end();       break;
149                 case KEY_CTRLED + KEY_C:    cli_clear();            break;
150                 case KEY_LEFT:              cli_cursor_left();      break;
151                 case KEY_RIGHT:             cli_cursor_right();     break;
152                 case KEY_BACKSP:            cli_cursor_backspace(); break;
153                 case KEY_CTRLED + KEY_D:
154                 case KEY_DELETE:            cli_cursor_del();       break;
155                 case KEY_UP:                cli_history_prev();     break;
156                 case KEY_DOWN:              cli_history_next();     break;
157                 case KEY_TAB:               cli_autocomplete();     break;
158                 case KEY_ENTER:             cli_execute();          break;
159                 case KEY_INSERT:
160                         CLI_insert_mode = !CLI_insert_mode;
161                         break;
162                 default:
163                         if (character == 255)
164                                 break;
165                         cli_add_character(character);
166                         break;
167         }
168         return 0;
169 }
170
171
172 /* Updates the console buffer */
173 static void con_update(void)
174 {
175         int loop;
176         int loop2;
177         int Screenlines;
178         grs_canvas *canv_save;
179
180         /* Due to the Blits, the update is not very fast: So only update if it's worth it */
181         if (!con_is_visible())
182                 return;
183
184         Screenlines = ConsoleSurface->cv_h / (CON_LINE_SPACE + ConsoleSurface->cv_font->ft_h);
185
186         canv_save = grd_curcanv;
187         gr_set_current_canvas(ConsoleSurface);
188
189         gr_set_fontcolor(CON_color, -1);
190
191         /* draw the background image if there is one */
192         if (BackgroundImage)
193                 gr_bitmap(0, 0, BackgroundImage);
194
195         // now draw text from last but second line to top
196         for (loop = 0; loop < Screenlines-1 && loop < LineBuffer - ConsoleScrollBack; loop++) {
197                 if (ConsoleScrollBack != 0 && loop == 0)
198                         for (loop2 = 0; loop2 < (VChars / 5) + 1; loop2++)
199                         {
200                                 gr_string(CON_CHAR_BORDER + (loop2*5*ConsoleSurface->cv_font->ft_w), (Screenlines - loop - 2) * (CON_LINE_SPACE + ConsoleSurface->cv_font->ft_h), CON_SCROLL_INDICATOR);
201                         }
202                 else
203                 {
204                         gr_string(CON_CHAR_BORDER, (Screenlines - loop - 2) * (CON_LINE_SPACE + ConsoleSurface->cv_font->ft_h), ConsoleLines[ConsoleScrollBack + loop]);
205                 }
206         }
207
208         gr_set_current_canvas(canv_save);
209 }
210
211
212 static void con_update_offset(void)
213 {
214         switch (Visible) {
215                 case CON_CLOSING:
216                         RaiseOffset -= CON_OPENCLOSE_SPEED;
217                         if(RaiseOffset <= 0) {
218                                 RaiseOffset = 0;
219                                 Visible = CON_CLOSED;
220                         }
221                         break;
222                 case CON_OPENING:
223                         RaiseOffset += CON_OPENCLOSE_SPEED;
224                         if(RaiseOffset >= ConsoleSurface->cv_h) {
225                                 RaiseOffset = ConsoleSurface->cv_h;
226                                 Visible = CON_OPEN;
227                         }
228                         break;
229                 case CON_OPEN:
230                 case CON_CLOSED:
231                         break;
232         }
233 }
234
235
236 /* Draws the console buffer to the screen if the console is "visible" */
237 void con_draw(void)
238 {
239         grs_canvas *canv_save;
240         grs_bitmap *clip;
241
242         /* only draw if console is visible: here this means, that the console is not CON_CLOSED */
243         if (Visible == CON_CLOSED)
244                 return;
245
246         /* Update the scrolling offset */
247         con_update_offset();
248
249         canv_save = grd_curcanv;
250
251         /* Update the command line since it has a blinking cursor */
252         gr_set_current_canvas(ConsoleSurface);
253
254         // restore InputBackground
255         gr_bitmap(0, ConsoleSurface->cv_h - ConsoleSurface->cv_font->ft_h, InputBackground);
256
257         cli_draw(ConsoleSurface->cv_h);
258
259         gr_set_current_canvas(&grd_curscreen->sc_canvas);
260
261         clip = gr_create_sub_bitmap(&ConsoleSurface->cv_bitmap, 0, ConsoleSurface->cv_h - RaiseOffset, ConsoleSurface->cv_w, RaiseOffset);
262
263         gr_bitmap(0, 0, clip);
264         gr_free_sub_bitmap(clip);
265
266         gr_set_current_canvas(canv_save);
267 }
268
269
270 void con_cmd_toggleconsole(int argc, char **argv)
271 {
272         if (argc > 1) {
273                 cmd_appendf("help %s", argv[0]);
274                 return;
275         }
276
277         if (con_is_visible())
278                 con_hide();
279         else
280                 con_show();
281 }
282
283
284 /* Initializes the console */
285 void con_init()
286 {
287         int loop;
288
289         Visible = CON_CLOSED;
290         RaiseOffset = 0;
291         ConsoleLines = NULL;
292         TotalConsoleLines = 0;
293         ConsoleScrollBack = 0;
294         BackgroundImage = NULL;
295
296         /* load the console surface */
297         ConsoleSurface = NULL;
298
299         /* Load the dirty rectangle for user input */
300         InputBackground = NULL;
301
302         VChars = CON_CHARS_PER_LINE - 1;
303         LineBuffer = CON_NUM_LINES;
304
305         ConsoleLines = (char **)d_malloc(sizeof(char *) * LineBuffer);
306         for (loop = 0; loop <= LineBuffer - 1; loop++) {
307                 ConsoleLines[loop] = (char *)d_calloc(CON_CHARS_PER_LINE, sizeof(char));
308         }
309
310         cli_init();
311         cmd_init();
312         cvar_init();
313
314         cmd_addcommand("toggleconsole", con_cmd_toggleconsole, "toggleconsole\n" "    show or hide the console");
315         cvar_registervariable (&con_threshold);
316
317         con_initialized = 1;
318
319         atexit(con_free);
320 }
321
322
323 void gr_init_bitmap_alloc( grs_bitmap *bm, int mode, int x, int y, int w, int h, int bytesperline);
324 void con_init_gfx(int w, int h)
325 {
326         int pcx_error;
327         grs_bitmap bmp;
328         ubyte pal[256*3];
329
330         if (ConsoleSurface) {
331                 /* resize console surface */
332                 gr_free_bitmap_data(&ConsoleSurface->cv_bitmap);
333                 gr_init_bitmap_alloc(&ConsoleSurface->cv_bitmap, BM_LINEAR, 0, 0, w, h, w);
334         } else {
335                 /* load the console surface */
336                 ConsoleSurface = gr_create_canvas(w, h);
337         }
338
339         CON_color = CON_DEFAULT_COLOR;
340
341         /* Load the consoles font */
342         con_font(CON_FONT);
343
344         /* make sure that the size of the console is valid */
345         if (w > grd_curscreen->sc_w || w < ConsoleSurface->cv_font->ft_w * 32)
346                 w = grd_curscreen->sc_w;
347         if (h > grd_curscreen->sc_h || h < ConsoleSurface->cv_font->ft_h)
348                 h = grd_curscreen->sc_h;
349
350         /* Load the dirty rectangle for user input */
351         if (InputBackground)
352                 gr_free_bitmap(InputBackground);
353         InputBackground = gr_create_bitmap(w, ConsoleSurface->cv_font->ft_h);
354
355         /* calculate the number of visible characters in the command line */
356 #if 0 // doesn't work because proportional font
357         VChars = (w - CON_CHAR_BORDER) / ConsoleSurface->cv_font->ft_w;
358         if (VChars >= CON_CHARS_PER_LINE)
359                 VChars = CON_CHARS_PER_LINE - 1;
360 #endif
361
362         gr_init_bitmap_data(&bmp);
363         pcx_error = pcx_read_bitmap(CON_BG, &bmp, BM_LINEAR, pal);
364         Assert(pcx_error == PCX_ERROR_NONE);
365         gr_remap_bitmap_good(&bmp, pal, -1, -1);
366         con_background(&bmp);
367         gr_free_bitmap_data(&bmp);
368 }
369
370
371 /* Makes the console visible */
372 void con_show(void)
373 {
374         Visible = CON_OPENING;
375         con_update();
376 }
377
378
379 /* Hides the console (make it invisible) */
380 void con_hide(void)
381 {
382         Visible = CON_CLOSING;
383         key_flush();
384 }
385
386
387 /* tells wether the console is visible or not */
388 int con_is_visible(void)
389 {
390         return((Visible == CON_OPEN) || (Visible == CON_OPENING));
391 }
392
393
394 /* Frees all the memory loaded by the console */
395 static void con_free(void)
396 {
397         int i;
398
399         for (i = 0; i <= LineBuffer - 1; i++) {
400                 d_free(ConsoleLines[i]);
401         }
402         d_free(ConsoleLines);
403
404         ConsoleLines = NULL;
405
406         if (ConsoleSurface)
407                 gr_free_canvas(ConsoleSurface);
408         ConsoleSurface = NULL;
409
410         if (BackgroundImage)
411                 gr_free_bitmap(BackgroundImage);
412         BackgroundImage = NULL;
413
414         if (InputBackground)
415                 gr_free_bitmap(InputBackground);
416         InputBackground = NULL;
417
418         con_initialized = 0;
419 }
420
421
422 /* Increments the console lines */
423 static void con_newline(void)
424 {
425         int loop;
426         char *temp;
427
428         temp = ConsoleLines[LineBuffer - 1];
429
430         for (loop = LineBuffer - 1; loop > 0; loop--)
431                 ConsoleLines[loop] = ConsoleLines[loop - 1];
432
433         ConsoleLines[0] = temp;
434
435         memset(ConsoleLines[0], 0, CON_CHARS_PER_LINE);
436         if (TotalConsoleLines < LineBuffer - 1)
437                 TotalConsoleLines++;
438         
439         //Now adjust the ConsoleScrollBack
440         //dont scroll if not at bottom
441         if(ConsoleScrollBack != 0)
442                 ConsoleScrollBack++;
443         //boundaries
444         if(ConsoleScrollBack > LineBuffer-1)
445                 ConsoleScrollBack = LineBuffer-1;
446 }
447
448
449 static inline int con_get_width(void)
450 {
451         if (!ConsoleSurface)
452                 return 0;
453
454         return ConsoleSurface->cv_bitmap.bm_w - CON_CHAR_BORDER;
455 }
456
457
458 static inline int con_get_string_width(char *string)
459 {
460         grs_canvas *canv_save;
461         int w = 0, h, aw;
462
463         if (!ConsoleSurface)
464                 return 0;
465
466         canv_save = grd_curcanv;
467         gr_set_current_canvas(ConsoleSurface);
468         gr_get_string_size(string, &w, &h, &aw);
469         gr_set_current_canvas(canv_save);
470
471         return w;
472 }
473
474
475 #ifdef _MSC_VER
476 # define vsnprintf _vsnprintf
477 #endif
478
479 /* Outputs text to the console (in game), up to CON_CHARS_PER_LINE chars can be entered */
480 static void con_out(const char *str, ...)
481 {
482         va_list marker;
483         //keep some space free for stuff like CON_Out("blablabla %s", Command);
484         char temp[CON_CHARS_PER_LINE + 128];
485         char* ptemp;
486
487         va_start(marker, str);
488         vsnprintf(temp, CON_CHARS_PER_LINE + 127, str, marker);
489         va_end(marker);
490
491         ptemp = temp;
492
493         // temp now contains the complete string we want to output
494         // the only problem is that temp is maybe longer than the console
495         // width so we have to cut it into several pieces
496
497         if (ConsoleLines) {
498                 char *p = ptemp;
499
500                 while (*p) {
501                         if (*p == '\n') {
502                                 *p = '\0';
503                                 con_newline();
504                                 strcat(ConsoleLines[0], ptemp);
505                                 ptemp = p+1;
506                         } else if (p - ptemp > VChars - strlen(ConsoleLines[0]) ||
507                                            con_get_string_width(ptemp) > con_get_width()) {
508                                 con_newline();
509                                 strncat(ConsoleLines[0], ptemp, VChars - strlen(ConsoleLines[0]));
510                                 ConsoleLines[0][VChars] = '\0';
511                                 ptemp = p;
512                         }
513                         p++;
514                 }
515                 if (strlen(ptemp)) {
516                         strncat(ConsoleLines[0], ptemp, VChars - strlen(ConsoleLines[0]));
517                         ConsoleLines[0][VChars] = '\0';
518                 }
519                 con_update();
520         }
521 }
522
523
524 /* Adds background image to the console, scaled to size of console*/
525 static int con_background(grs_bitmap *image)
526 {
527         /* Free the background from the console */
528         if (image == NULL) {
529                 if (BackgroundImage)
530                         gr_free_bitmap(BackgroundImage);
531                 BackgroundImage = NULL;
532                 return 0;
533         }
534
535         /* Load a new background */
536         if (BackgroundImage)
537                 gr_free_bitmap(BackgroundImage);
538         BackgroundImage = gr_create_bitmap(ConsoleSurface->cv_w, ConsoleSurface->cv_h);
539         gr_bitmap_scale_to(image, BackgroundImage);
540
541         gr_bm_bitblt(BackgroundImage->bm_w, InputBackground->bm_h, 0, 0, 0, ConsoleSurface->cv_h - ConsoleSurface->cv_font->ft_h, BackgroundImage, InputBackground);
542
543         return 0;
544 }
545
546
547 /* Sets font info for the console */
548 static void con_font(grs_font *font)
549 {
550         grs_canvas *canv_save;
551
552         canv_save = grd_curcanv;
553         gr_set_current_canvas(ConsoleSurface);
554         gr_set_curfont(font);
555         gr_set_current_canvas(canv_save);
556 }
557
558
559 static void con_clear(void)
560 {
561         int loop;
562         
563         for (loop = 0; loop <= LineBuffer - 1; loop++)
564                 memset(ConsoleLines[loop], 0, CON_CHARS_PER_LINE);
565 }
566
567
568 /* convert to ansi rgb colors 17-231 */
569 #define PAL2ANSI(x) ((36*gr_palette[(x)*3]/11) + (6*gr_palette[(x)*3+1]/11) + (gr_palette[(x)*3+2]/11) + 16)
570
571 /* Print a message to the console */
572 void con_printf(int priority, char *fmt, ...)
573 {
574         va_list arglist;
575         char buffer[2048];
576
577         if (priority <= (con_threshold.intval))
578         {
579                 va_start (arglist, fmt);
580                 vsprintf (buffer,  fmt, arglist);
581                 va_end (arglist);
582
583                 if (con_initialized)
584                         con_out(buffer);
585
586                 if (!text_console_enabled)
587                         return;
588
589                 if (isatty(fileno(stdout))) {
590                         char *buf, *p;
591                         unsigned char color, spacing, underline;
592
593                         p = buf = buffer;
594                         do
595                                 switch (*p)
596                                 {
597                                 case CC_COLOR:
598                                         *p++ = 0;
599                                         printf("%s", buf);
600                                         color = *p++;
601                                         printf("\x1B[38;5;%dm", PAL2ANSI(color));
602                                         buf = p;
603                                         break;
604                                 case CC_LSPACING:
605                                         *p++ = 0;
606                                         printf("%s", buf);
607                                         spacing = *p++;
608                                         //printf("<SPACING %d>", color);
609                                         buf = p;
610                                         break;
611                                 case CC_UNDERLINE:
612                                         *p++ = 0;
613                                         printf("%s", buf);
614                                         underline = 1;
615                                         //printf("<UNDERLINE>");
616                                         buf = p;
617                                         break;
618                                 default:
619                                         p++;
620                                 }
621                         while (*p);
622
623                         printf("%s", buf);
624
625                 } else {
626                         /* Produce a sanitised version and send it to the standard output */
627                         char *p1, *p2;
628
629                         p1 = p2 = buffer;
630                         do
631                                 switch (*p1)
632                                 {
633                                 case CC_COLOR:
634                                 case CC_LSPACING:
635                                         p1++;
636                                 case CC_UNDERLINE:
637                                         p1++;
638                                         break;
639                                 default:
640                                         *p2++ = *p1++;
641                                 }
642                         while (*p1);
643                         *p2 = 0;
644
645                         printf("%s", buffer);
646                 }
647         }
648 }