console: Format tabs semi-intelligently
[attac-man.git] / oglconsole.c
blob20df060ca82ba29d8cd80376a8482eb96af4e8f0
1 /* oglconsole -- gpl license here */
3 /* This strategy seems to offer the convenience of zero-configuration, but
4 * obviously it also offers defining GLHEADERINCLUDE */
5 #ifdef GLHEADERINCLUDE
6 # include GLHEADERINCLUDE
7 #else
8 # ifdef __MACH__
9 # include <OpenGL/gl.h>
10 # else
11 # include <GL/gl.h>
12 # endif
13 #endif
15 #include "oglconsole.h"
17 #include <string.h>
18 #include <stdarg.h>
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <sys/time.h>
23 #define C ((_OGLCONSOLE_Console*)console)
25 /* OGLCONSOLE font */
26 #include "ConsoleFont.c"
27 #define FIRST_CHARACTER ' '
28 #define LAST_CHARACTER '~'
30 #define CHAR_PIXEL_W 6
31 #define CHAR_PIXEL_H 13
32 #define CHAR_WIDTH 0.0234375 /* ogl tex coords */
33 #define CHAR_HEIGHT 0.203125 /* ogl tex coords */
35 /* This is how long the animation should take to make the transition between
36 * "hidden" and "visible" console visibility modes (expressed in milliseconds) */
37 #define SLIDE_MS 700
39 /* If we don't know how to retrieve the time then we can just use a number of
40 * frames to divide up the time it takes to transition between "hidden" and
41 * "visible" console visibility modes */
42 #define SLIDE_STEPS 5
44 GLuint OGLCONSOLE_glFontHandle = 0;
45 int
46 OGLCONSOLE_CreateFont(void)
49 int err = glGetError();
50 if (err)
51 printf("GL ERROR: %i\n", err);
53 #ifdef DEBUG
54 puts("Creating OGLCONSOLE font");
55 #endif
57 /* Get a font index from OpenGL */
58 glGenTextures(1, &OGLCONSOLE_glFontHandle);
60 int err = glGetError();
61 if (err)
62 printf("glGenTextures() error: %i\n", err);
65 /* Select our font */
66 glBindTexture(GL_TEXTURE_2D, OGLCONSOLE_glFontHandle);
68 int err = glGetError();
69 if (err)
70 printf("glBindTexture() error: %i\n", err);
73 /* Set some parameters i guess */
74 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
75 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
77 /* Upload our font */
78 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
79 OGLCONSOLE_FontData.width, OGLCONSOLE_FontData.height, 0,
80 GL_RGB, GL_UNSIGNED_BYTE, OGLCONSOLE_FontData.pixel_data);
83 int err = glGetError();
84 if (err)
85 printf("glTexImage2D() error: %i\n", err);
88 #ifdef DEBUG
89 puts("Created OGLCONSOLE font");
90 #endif
91 return 1;
94 /* TODO: Expose these macros to the user? */
96 /* This is the longest command line that the user can enter TODO: Make dynamic
97 * so that the user can enter any length line */
98 #define MAX_INPUT_LENGTH 256
100 /* This is the number of command line entries that the console will remember (so
101 * that the user can use the up/down keys to see and easily re-execute his past
102 * commands) */
103 #define MAX_HISTORY_COUNT 25
105 #define MAX_COMMAND_ARGS 20
107 /* This is the default number of lines for the console to remember (that is to
108 * say, the user can scroll up and down to see what has been printed to the
109 * console in the past, and this is the number of those lines, plus the number
110 * of lines shown on the screen at all times) */
111 #define DEFAULT_MAX_LINES 100
113 /* OGLCONSOLE console structure */
114 typedef struct {
115 GLdouble mvMatrix[16];
116 int mvMatrixUse;
118 GLdouble pMatrix[16];
119 int pMatrixUse;
121 /* Screen+scrollback lines (console output) */
122 char *lines;
123 int lines_size;
124 int maxLines, lineQueueIndex, lineScrollIndex;
126 /* History scrollback (command input) */
127 char history[MAX_HISTORY_COUNT][MAX_INPUT_LENGTH];
128 int historyQueueIndex, historyScrollIndex;
130 /* Current input line */
131 char inputLine[MAX_INPUT_LENGTH];
132 int inputCursorPos, inputLineLength;
134 /* Rows and columns of text to display */
135 int textWidth, textHeight;
137 /* Current column of text where new text will be inserted */
138 char *outputCursor;
139 int outputNewline;
141 /* Width and height of a single character for the GL */
142 GLdouble characterWidth, characterHeight;
144 /* Basic options */
145 int visibility;
147 /* Various callback functions defined by the user */
148 void (*enterKeyCallback) (OGLCONSOLE_Console console, int argc, char **argv);
150 /* When not visible, render the last line at the top of the screen
151 * for a bit. */
152 struct timeval expire;
153 int frame_skip;
155 } _OGLCONSOLE_Console;
157 /* To save code, I've gone with an imperative "modal" kind of interface */
158 _OGLCONSOLE_Console *programConsole = NULL;
160 /* This console is the console currently receiving user input */
161 _OGLCONSOLE_Console *userConsole = NULL;
163 /* Set the callback for a console */
164 void
165 OGLCONSOLE_EnterKey(void (*cbfun) (OGLCONSOLE_Console console, int argc, char **argv))
167 programConsole->enterKeyCallback = cbfun;
170 void
171 OGLCONSOLE_DefaultEnterKeyCallback(OGLCONSOLE_Console console, int __attribute__((unused)) argc, char __attribute__((unused)) **argv)
173 OGLCONSOLE_Output(console,
174 "No enter key callback is registered for this console!\n");
178 void
179 OGLCONSOLE_InitText(void *c, int res_x, int res_y)
181 _OGLCONSOLE_Console *console = c;
182 char *new_lines;
183 int new_lines_size;
185 if (!console) {
186 console = userConsole;
187 if (!userConsole)
188 return;
191 /* Textual dimensions */
193 console->textWidth = res_x / CHAR_PIXEL_W;
194 console->textHeight = res_y / CHAR_PIXEL_H;
195 console->characterWidth = 1.0 / console->textWidth;
196 console->characterHeight = 1.0 / console->textHeight;
198 /* Different values have different meanings for xMatrixUse:
199 0) Do not change the matrix before rendering
200 1) Upload the console's matrix before rendering
201 2) Multiply the console's matrix before rendering */
203 /* Initialize its projection matrix */
204 console->pMatrixUse = 1;
205 glMatrixMode(GL_PROJECTION);
206 glPushMatrix();
207 glOrtho(0, console->textWidth, 0, console->textHeight, -1, 1);
208 glGetDoublev(GL_MODELVIEW_MATRIX, console->pMatrix);
209 glPopMatrix();
211 /* Initialize its modelview matrix */
212 console->mvMatrixUse = 1;
213 glMatrixMode(GL_MODELVIEW);
214 glPushMatrix();
215 glLoadIdentity();
216 glTranslated(-1, -1, 0);
217 glScaled(2, 2, 1);
218 glGetDoublev(GL_MODELVIEW_MATRIX, console->mvMatrix);
219 glPopMatrix();
221 /* Screen and scrollback lines */
222 /* This is the total number of screen lines in memory (start blank) */
223 console->maxLines = DEFAULT_MAX_LINES;
225 /* (re)allocate space for text */
226 new_lines_size = console->maxLines * console->textWidth + 1;
227 new_lines = malloc(new_lines_size);
228 /* XXX return value */
229 memset(new_lines, 0, new_lines_size);
231 if (console->lines) {
232 #if 0
233 if (new_lines_size >= console->lines_size) {
234 memcpy(new_lines, console->lines, console->lines_size);
235 } else {
236 memcpy(new_lines,
237 console->lines + (console->lines_size-new_lines_size),
238 new_lines_size);
240 #endif
242 free(console->lines);
245 console->lines_size = new_lines_size;
246 console->lines = new_lines;
248 /* This cursor points to the X pos where console output is next destined */
249 console->outputCursor = console->lines;
251 /* This variable represents whether or not a newline has been left */
252 console->outputNewline = 0;
253 /* This cursor points to the X pos where console output is next destined */
254 console->outputCursor = console->lines;
255 /* This cursor points to what line console output is next destined for */
256 console->lineQueueIndex = 0;
257 /* This cursor points to what line the console view is scrolled to */
258 console->lineScrollIndex = console->maxLines - console->textHeight + 1;
260 /* Initialize the user's input (command line) */
261 console->inputLineLength = 0;
262 console->inputCursorPos = 0;
263 console->inputLine[0] = '\0';
265 OGLCONSOLE_Output((void *) console,
266 "Console Resolution: %i x %i\n"
267 "Text Size: %i x %i\n",
268 res_x, res_y,
269 console->textWidth, console->textHeight);
272 OGLCONSOLE_Console
273 OGLCONSOLE_Create()
275 _OGLCONSOLE_Console *console;
276 GLint viewport[4];
278 /* If font hasn't been created, we create it */
279 if (!glIsTexture(OGLCONSOLE_glFontHandle))
280 OGLCONSOLE_CreateFont();
282 /* Allocate memory for our console */
283 console = (void *) malloc(sizeof (_OGLCONSOLE_Console));
284 memset(console, 0, sizeof(*console));
286 console->maxLines = DEFAULT_MAX_LINES;
288 glGetIntegerv(GL_VIEWPORT, viewport);
289 OGLCONSOLE_InitText(console, viewport[2], viewport[3]);
291 /* Callbacks */
292 console->enterKeyCallback = OGLCONSOLE_DefaultEnterKeyCallback;
294 /* The console starts life invisible */
295 console->visibility = 0;
297 /* If no consoles existed before, we select this one for convenience */
298 if (!programConsole)
299 programConsole = console;
300 if (!userConsole)
301 userConsole = console;
303 /* Temporary shit */
304 OGLCONSOLE_Output((void *) console,
305 "Console input length: %i\n", MAX_INPUT_LENGTH);
306 OGLCONSOLE_Output((void *) console, "Console initialized\n");
308 /* Return the opaque pointer to the programmer */
309 return (OGLCONSOLE_Console) console;
312 /* This functoin is only used internally; the user ultimately invokes this
313 * function through either a call to Destroy() or Quit(); the purpose of this
314 * mechanism is to warn the user if he has explicitly destroyed a console that
315 * was engaged in operation at the time they destroyed it (the only two
316 * operations a console can be engaged in are receiving programmer interaction,
317 * or receiving end-user interaction. fyi, "user" always refers to the
318 * programmer, end-user refers to the real end-user) */
319 void
320 OGLCONSOLE_DestroyReal(OGLCONSOLE_Console console, int warn)
322 free(C);
324 if (warn) {
325 if (programConsole == C) {
326 fprintf(stderr,
327 "Warning: OGLCONSOLE you just destroyed the programConsole!\n");
328 programConsole = NULL;
331 if (userConsole == C) {
332 fprintf(stderr,
333 "Warning: OGLCONSOLE you just destroyed the userConsole!\n");
334 userConsole = NULL;
339 /* The user can call this function to free a console. A warning is printed to
340 * stderr if the user has destroyed a console destroyed while it was receiving
341 * input, see DestroyReal() for details TODO: there are currently no semantics
342 * under consideration for explicitly destroying an 'engaged' console WITHOUT
343 * having a warning issued, nor is there a way to deselect a console without
344 * also selecting a new one as of yet */
345 void
346 OGLCONSOLE_Destroy(OGLCONSOLE_Console console)
348 OGLCONSOLE_DestroyReal(console, 1);
351 /* This function frees all of the consoles that the library is actively aware
352 * of, and is intended to be used by programs that have only a single console;
353 * technically this particular function will free up to two consoles, but don't
354 * count on it because that may change; no warnings are issued by this function */
355 void
356 OGLCONSOLE_Quit()
358 OGLCONSOLE_DestroyReal((void *) programConsole, 0);
359 if (programConsole != userConsole)
360 OGLCONSOLE_DestroyReal((void *) userConsole, 0);
361 programConsole = NULL;
362 userConsole = NULL;
365 /* Select a console to receive input from the user (read: programmer); this
366 * function is intended for use by applications which create MORE THAN ONE
367 * console, since every newly created console is automatically selected for
368 * programmer input ('programmer input' refers to calling any function which
369 * does not specify a console explicitly in its parameter list. the console
370 * which is 'engaged' by 'programmer input' at the time of calling one of these
371 * functions is the console that function will operate on */
372 void
373 OGLCONSOLE_EditConsole(OGLCONSOLE_Console console)
375 programConsole = C;
378 /* Show or hide a console */
379 void
380 OGLCONSOLE_SetVisibility(int __attribute__ ((unused)) visible)
384 /* Get current configuration information about a console */
385 void
386 OGLCONSOLE_Info(void)
388 puts("TODO: print some console info here");
391 /* This routine is meant for applications with a single console, if you use
392 * multiple consoles in your program, use Render() instead */
393 void
394 OGLCONSOLE_Draw()
396 OGLCONSOLE_Render((void *) userConsole);
399 /* Internal functions for drawing text. You don't want these, do you? */
400 void OGLCONSOLE_DrawString(char *s, double x, double y, double w, double h,
401 double z);
402 void OGLCONSOLE_DrawWrapString(char *s, double x, double y, double w, double h,
403 double z, int wrap);
404 void OGLCONSOLE_DrawCharacter(int c, double x, double y, double w, double h,
405 double z);
407 static void
408 OGLCONSOLE_RenderText(OGLCONSOLE_Console console)
410 /* Graphical line, and line in lines[] */
411 int gLine, tLine = C->lineScrollIndex;
413 /* Iterate through each line being displayed */
414 for (gLine = 0; gLine < C->textHeight; gLine++) {
415 /* Draw this line of text adjusting for user scrolling up/down */
416 OGLCONSOLE_DrawString(C->lines + (tLine * C->textWidth),
418 (C->textHeight -
419 gLine) * C->characterHeight,
420 C->characterWidth,
421 C->characterHeight, 0);
423 /* Grab next line of text using wheel-queue wrapping */
424 if (++tLine >= C->maxLines)
425 tLine = 0;
428 /* Here we draw the current commandline, it will either be a line from
429 * the command history or the line being edited atm */
430 if (C->historyScrollIndex >= 0) {
431 glColor3d(1, 0, 0);
432 OGLCONSOLE_DrawString(C->history[C->historyScrollIndex],
433 0, 0,
434 C->characterWidth,
435 C->characterHeight, 0);
436 } else {
437 /* Draw input line cyan */
438 glColor3d(0, 1, 1);
439 OGLCONSOLE_DrawString(C->inputLine,
440 0, 0,
441 C->characterWidth,
442 C->characterHeight, 0);
444 /* Draw cursor beige */
445 glColor3d(1, 1, .5);
446 OGLCONSOLE_DrawCharacter('_' - ' ',
447 C->inputCursorPos *
448 C->characterWidth, 0,
449 C->characterWidth,
450 C->characterHeight, 0);
454 static void
455 OGLCONSOLE_RenderLast(OGLCONSOLE_Console console)
457 struct timeval tv;
459 if (!C->expire.tv_sec)
460 return;
462 /* don't do this time of day thing too often */
463 if (++C->frame_skip % 8) {
464 gettimeofday(&tv, NULL);
465 if (tv.tv_sec > C->expire.tv_sec ||
466 (tv.tv_sec == C->expire.tv_sec &&
467 tv.tv_usec >= C->expire.tv_usec)) {
468 C->expire.tv_sec = 0;
469 C->frame_skip = 0;
470 return;
474 /* Graphical line, and line in lines[] */
475 OGLCONSOLE_DrawString(C->lines + (C->textWidth * C->lineQueueIndex),
476 0, 0, C->characterWidth,
477 C->characterHeight, 0);
480 /* This function draws a single specific console; if you only use one console in
481 * your program, use Draw() instead */
482 void
483 OGLCONSOLE_Render(OGLCONSOLE_Console console)
485 double d;
487 /* Don't render hidden console */
488 if (C->visibility == 0 && !C->expire.tv_sec)
489 return;
491 glMatrixMode(GL_PROJECTION);
492 glPushMatrix();
493 glLoadMatrixd(C->pMatrix);
495 glMatrixMode(GL_MODELVIEW);
496 glPushMatrix();
497 glLoadMatrixd(C->mvMatrix);
499 glPushAttrib(GL_ALL_ATTRIB_BITS);
501 /* glEnable(GL_BLEND);
502 glBlendFunc(GL_ONE, GL_ONE);*/
504 /* TODO: This SHOULD become an option at some point because the
505 * infrastructure for "real" consoles in the game (like you could walk up to
506 * a computer terminal and manipulate a console on a computer using
507 * oglconsole) already exists; you'd want depth testing in that case */
508 glDisable(GL_DEPTH_TEST);
510 /* Render hiding / showing console in a special manner. Zero means hidden. 1
511 * means visible. All other values are traveling toward zero or one. TODO:
512 * Make this time dependent */
513 if (C->visibility != 1 && C->visibility != 0) {
514 int v = C->visibility;
516 /* Count down in both directions */
517 if (v < 0) {
518 v ^= -1;
519 C->visibility++;
520 } else {
521 v = SLIDE_STEPS - v;
522 C->visibility--;
525 d = (1.0 / (double)SLIDE_STEPS) * v;
526 glTranslated(0, 1.0 - d, 0);
527 } else if (C->visibility == 0) {
528 /* Draw the last line */
529 glTranslated(0, 1 - (1 / (double)C->textHeight), 0);
533 /* First we draw our console's background TODO: Add something fancy? */
534 glDisable(GL_TEXTURE_2D);
535 glEnable(GL_BLEND);
536 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
537 // glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
538 glColor4d(.1, 0, 0, 0.5);
540 glBegin(GL_QUADS);
541 glVertex3d(0, 0, 0);
542 glVertex3d(1, 0, 0);
543 glVertex3d(1, 1, 0);
544 glVertex3d(0, 1, 0);
545 glEnd();
547 // Change blend mode for drawing text
548 glBlendFunc(GL_ONE, GL_ONE);
550 /* Select the console font */
551 glEnable(GL_TEXTURE_2D);
552 glBindTexture(GL_TEXTURE_2D, OGLCONSOLE_glFontHandle);
554 /* Recolor text */
555 glColor3d(0, 1, 0);
557 /* Render console contents */
558 glBegin(GL_QUADS);
559 if (C->visibility)
560 OGLCONSOLE_RenderText(console);
561 else
562 OGLCONSOLE_RenderLast(console);
564 glEnd();
566 /* Relinquish our rendering settings */
567 glMatrixMode(GL_PROJECTION);
568 glPopMatrix();
570 glMatrixMode(GL_MODELVIEW);
571 glPopMatrix();
573 glPopAttrib();
576 /* Issue rendering commands for a single a string */
577 void
578 OGLCONSOLE_DrawString(char *s, double x, double y, double w, double h, double z)
580 while (*s) {
581 OGLCONSOLE_DrawCharacter(*s - ' ', x, y, w, h, z);
582 s++;
583 x += w;
587 /* Issue rendering commands for a single a string */
588 void
589 OGLCONSOLE_DrawWrapString(char *s, double x, double y, double w, double h,
590 double z, int wrap)
592 int pos = 0;
593 double X = x;
595 while (*s) {
596 OGLCONSOLE_DrawCharacter(*s - ' ', X, y, w, h, z);
597 s++;
598 X += w;
600 if (++pos >= wrap) {
601 pos = 0;
602 y += h;
603 X = x;
608 /* Issue rendering commands for a single character */
609 void
610 OGLCONSOLE_DrawCharacter(int c, double x, double y, double w, double h,
611 double z)
613 // static int message = 0;
614 double cx, cy, cX, cY;
616 // if (c < FIRST_CHARACTER || c > LAST_CHARACTER)
617 // c = (c - FIRST_CHARACTER) % (LAST_CHARACTER - FIRST_CHARACTER);
618 // else c -= FIRST_CHARACTER;
620 cx = (c % 42) * CHAR_WIDTH;
621 cy = 1.0 - (c / 42) * CHAR_HEIGHT;
622 cX = cx + CHAR_WIDTH;
623 cY = 1.0 - (c / 42 + 1) * CHAR_HEIGHT;
625 /* if (message != c)
627 printf("For %i we got %f, %f\n", c, x, y);
628 message = c;
631 /* This should occur outside of this function for optimiation TODO: MOVE IT */
632 glTexCoord2d(cx, cy);
633 glVertex3d(x, y, z);
634 glTexCoord2d(cX, cy);
635 glVertex3d(x + w, y, z);
636 glTexCoord2d(cX, cY);
637 glVertex3d(x + w, y + h, z);
638 glTexCoord2d(cx, cY);
639 glVertex3d(x, y + h, z);
642 /* This is the final, internal function for printing text to a console */
643 void
644 OGLCONSOLE_Output(OGLCONSOLE_Console console, const char *s, ...)
646 va_list argument;
648 /* cache some console properties */
649 int lineQueueIndex = C->lineQueueIndex;
650 int lineScrollIndex = C->lineScrollIndex;
651 int textWidth = C->textWidth;
652 int maxLines = C->maxLines;
653 int i = 0, o = 0, tabcount = 0;
655 /* String buffer */
656 char intermediate[2048];
657 char output[2048];
659 /* string copy cursors */
660 char *consoleCursor, *outputCursor = output;
662 /* Acrue arguments in argument list */
663 va_start(argument, s);
664 vsnprintf(intermediate, sizeof(intermediate), s, argument);
665 va_end(argument);
667 /* Expand tabs if any exist */
668 while(intermediate[i]) {
669 if (intermediate[i] != '\t') {
670 output[o++] = intermediate[i];
671 i++;
672 continue;
675 /* Count tabs */
676 while (intermediate[i] == '\t') {
677 i++;
678 tabcount++;
681 /* format to that many tab stops */
682 do {
683 output[o++] = ' ';
684 } while (o % (tabcount*8));
686 output[o] = 0;
688 /* This cursor tells us where in the console display we are currently
689 * copying text into from the "output" string */
690 consoleCursor = C->outputCursor;
692 while (*outputCursor) {
693 /* Here we check to see if any conditions require console line
694 * advancement. These two conditions are:
695 1) Hitting the end of the screen
696 2) Getting a newlien character */
697 if ((C->outputNewline) ||
698 (consoleCursor - (C->lines + lineQueueIndex * textWidth))
699 >= (textWidth - 1)) {
700 C->outputNewline = 0;
702 //puts("incrementing to the next line");
704 /* Inrement text-line index, with wrapping */
705 if (++lineQueueIndex >= maxLines)
706 lineQueueIndex = 0;
708 /* Scroll the console display one line TODO: Don't scroll if the console is
709 * currently scrolled away from the end of output? */
710 if (++lineScrollIndex >= maxLines)
711 lineScrollIndex = 0;
713 /* Reposition the cursor at the beginning of the new line */
714 consoleCursor =
715 C->lines + lineQueueIndex * C->textWidth;
718 /* If we encounter a newline character, we set the newline flag, which
719 * tells the console to advance one line before it prints the next
720 * character. The reason we do it this way is to defer line-advancement,
721 * and thus we needn't suffer through a needless blank line between
722 * console output and the command line, wasting precious screen
723 * real-estate */
724 if (*outputCursor == '\n') {
725 C->outputNewline = 1;
726 outputCursor++;
727 continue;
730 /* copy a single character */
731 *(consoleCursor++) = *(outputCursor++);
734 /* Unless we're at the very end of our current line, we finish up by capping
735 * a NULL terminator on the current line */
736 if (consoleCursor != C->lines + (lineQueueIndex + 1) * C->textWidth - 1)
737 *consoleCursor = '\0';
739 /* Restore cached values */
740 C->lineQueueIndex = lineQueueIndex;
741 C->lineScrollIndex = lineScrollIndex;
742 C->outputCursor = consoleCursor; // TODO: confusing variable names
744 /* old way of copying the text into the console */
745 //strcpy(C->lines[C->lineQueueIndex], output);
746 #ifdef DEBUG
747 printf("Copied \"%s\" into line %i\n", output, C->lineQueueIndex);
748 #endif
749 gettimeofday(&C->expire, NULL);
750 C->expire.tv_sec += 3;
754 /* Mono-Console Users: print text to the console; multi-console users use
755 * Output() */
756 void
757 OGLCONSOLE_Print(char *s, ...)
759 va_list argument;
760 char output[4096];
762 /* Acrue arguments in argument list */
763 va_start(argument, s);
764 vsnprintf(output, 4096, s, argument);
765 va_end(argument);
767 /* TODO: Find some way to pass the va_list arguments to OGLCONSOLE_Output
768 * so that we don't waste extra time with the "%s" bullshit */
769 OGLCONSOLE_Output((OGLCONSOLE_Console) userConsole, "%s", output);
772 #if 0
773 /* Multi-Console Users: print text to a specific console; mono-console users use
774 * Print() */
775 void
776 OGLCONSOLE_Output(OGLCONSOLE_Console console, char *s)
778 /* TODO: Chop up output to wrap text */
780 /* TODO: Add auto-scroll (the commented out code here is a failed attempt,
781 * my brain is too scattered to do it) */
782 /* If the console isn't scrolled up, then we move the scroll point */
783 /* if (C->lineQueueIndex - C->lineScrollIndex == C->textHeight)
784 if (++C->lineScrollIndex - C->lineScrollIndex >= MAX_LINE_COUNT)
785 C->lineScrollIndex = 0;*/
788 #endif
790 /* Internal encapsulation of the act for adding a command the user executed to
791 * their command history for that particular console */
792 void
793 OGLCONSOLE_AddHistory(_OGLCONSOLE_Console * console, char *s)
795 if (++C->historyQueueIndex > MAX_HISTORY_COUNT)
796 C->historyQueueIndex = 0;
798 strcpy(C->history[C->historyQueueIndex], s);
801 void
802 OGLCONSOLE_YankHistory(_OGLCONSOLE_Console * console)
804 /* First we have to see if we are browsing our command history */
805 if (console->historyScrollIndex != -1) {
806 /* Copy the selected command into the current input line */
807 strcpy(console->inputLine,
808 console->history[console->historyScrollIndex]);
810 /* Set up this shite */
811 console->inputCursorPos =
812 console->inputLineLength = strlen(console->inputLine);
814 /* Drop out of history browsing mode */
815 console->historyScrollIndex = -1;
819 #ifndef OGLCONSOLE_USE_SDL
820 #error **************************************************************************
821 #error
822 #error
823 #error
824 #error Only SDL is supported so far: you must define the OGLCONSOLE_USE_SDL macro
825 #error
826 #error
827 #error
828 #error **************************************************************************
829 #endif
831 /* This function tries to handle the incoming SDL event. In the future there may
832 * be non-SDL analogs for input systems such as GLUT. Returns true if the event
833 * was handled by the console. If console is hidden, no events are handled. */
834 #ifdef OGLCONSOLE_USE_SDL
835 #include "SDL.h"
836 #define KEY_BACKSPACE SDLK_BACKSPACE
837 #define KEY_DELETE SDLK_DELETE
838 #define KEY_RETURN SDLK_RETURN
839 #define KEY_UP SDLK_UP
840 #define KEY_DOWN SDLK_DOWN
841 #define KEY_LEFT SDLK_LEFT
842 #define KEY_RIGHT SDLK_RIGHT
843 #define KEY_PAGEUP SDLK_PAGEUP
844 #define KEY_PAGEDOWN SDLK_PAGEDOWN
845 #define KMOD_CAPITALIZE (KMOD_LSHIFT|KMOD_RSHIFT|KMOD_CAPS)
847 OGLCONSOLE_SDLEvent(SDL_Event * e)
849 /* If the terminal is hidden we only check for show/hide key */
850 if (userConsole->visibility < 1) {
851 if (e->type == SDL_KEYDOWN && e->key.keysym.sym == '`') {
852 // TODO: Fetch values from OS?
853 // TODO: Expose them to the program
854 SDL_EnableKeyRepeat(250, 30);
855 userConsole->visibility += SLIDE_STEPS;
856 return 1;
859 return 0;
862 /* TODO: SDL_KEYPRESS? ENABLE KEY REPEAT USING THE HIDE/SHOW FUNCTION? */
863 if (e->type == SDL_KEYDOWN) {
864 /* Reject most modifier keys TODO: Add some accelerator keys? */
865 if (e->key.keysym.mod & ~(KMOD_CAPITALIZE | KMOD_NUM))
866 return 0;
868 /* Check for hide key */
869 if (e->key.keysym.sym == '`' || e->key.keysym.sym == SDLK_ESCAPE) {
870 /* Tell console to slide into closing */
871 userConsole->visibility -= SLIDE_STEPS;
873 /* Disable key repeat */
874 SDL_EnableKeyRepeat(0, 0);
875 return 1;
878 /* TODO: Find out how to handle CAPSLOCK */
879 if (e->key.keysym.sym >= ' ' && e->key.keysym.sym <= '~') {
880 int k = e->key.keysym.sym;
881 char *c, *d;
883 /* Yank the command history if necessary */
884 OGLCONSOLE_YankHistory(userConsole);
886 /* Capitalize if necessary */
887 if (e->key.keysym.mod & KMOD_CAPITALIZE) {
888 static
889 const int capital[] =
890 { (int) ' ', (int) '!', (int) '"',
891 (int) '#',
892 (int) '$', (int) '%', (int) '&',
893 (int) '"', (int) '(', (int) ')',
894 (int) '*', (int) '+', (int) '<',
895 (int) '_', (int) '>', (int) '?',
896 (int) ')', (int) '!', (int) '@',
897 (int) '#', (int) '$', (int) '%',
898 (int) '^', (int) '&', (int) '*',
899 (int) '(', (int) ':', (int) ':',
900 (int) '<', (int) '+', (int) '>',
901 (int) '?', (int) '@', (int) 'A',
902 (int) 'B', (int) 'C', (int) 'D',
903 (int) 'E', (int) 'F', (int) 'G',
904 (int) 'H', (int) 'I', (int) 'J',
905 (int) 'K', (int) 'L', (int) 'M',
906 (int) 'N', (int) 'O', (int) 'P',
907 (int) 'Q', (int) 'R', (int) 'S',
908 (int) 'T', (int) 'U', (int) 'V',
909 (int) 'W', (int) 'X', (int) 'Y',
910 (int) 'Z', (int) '{', (int) '|',
911 (int) '}', (int) '^', (int) '_',
912 (int) '~', (int) 'A', (int) 'B',
913 (int) 'C', (int) 'D', (int) 'E',
914 (int) 'F', (int) 'G', (int) 'H',
915 (int) 'I', (int) 'J', (int) 'K',
916 (int) 'L', (int) 'M', (int) 'N',
917 (int) 'O', (int) 'P', (int) 'Q',
918 (int) 'R', (int) 'S', (int) 'T',
919 (int) 'U', (int) 'V', (int) 'W',
920 (int) 'X', (int) 'Y', (int) 'Z',
921 (int) '{', (int) '|', (int) '}',
922 (int) '~'
925 /* If we're not explicitly holding a shift key, that means just
926 * capslock, which means we only capitalize letters */
927 if ((k >= 'a' && k <= 'z')
928 || (e->key.keysym.mod & KMOD_SHIFT))
929 k = capital[k - ' '];
932 /* Point to the cursor position and the end of the string */
933 c = userConsole->inputLine +
934 userConsole->inputCursorPos;
935 d = userConsole->inputLine +
936 userConsole->inputLineLength + 1;
938 /* Slide some of the string to the right */
939 for (; d != c; d--)
940 *d = *(d - 1);
942 /* Insert new character */
943 *c = k;
945 /* Increment input line length counter */
946 userConsole->inputLineLength++;
948 /* Advance input cursor position */
949 userConsole->inputCursorPos++;
951 return 1;
954 else if (e->key.keysym.sym == KEY_DELETE
955 || e->key.keysym.sym == KEY_BACKSPACE) {
956 /* Yank the command history if necessary */
957 OGLCONSOLE_YankHistory(userConsole);
959 /* If string is not empty */
960 if (userConsole->inputLineLength) {
961 char *end, *c;
963 /* Backspace is like pressing LEFT and then DELETE */
964 if (e->key.keysym.sym == KEY_BACKSPACE)
965 userConsole->inputCursorPos--;
967 /* With delete we much check more than just inputLineLength */
968 else if (userConsole->inputCursorPos ==
969 userConsole->inputLineLength)
970 return 1;
972 c = userConsole->inputLine +
973 userConsole->inputCursorPos;
974 end =
975 userConsole->inputLine +
976 --userConsole->inputLineLength;
978 while (c <= end) {
979 *c = *(c + 1);
980 c++;
984 return 1;
987 else if (e->key.keysym.sym == KEY_RETURN) {
988 char *argv[MAX_COMMAND_ARGS];
989 int argc = 0;
991 /* Yank the command history if necessary */
992 OGLCONSOLE_YankHistory(userConsole);
994 /* Add user's command to history */
995 OGLCONSOLE_AddHistory(userConsole,
996 userConsole->inputLine);
998 /* Print user's command to the console */
999 OGLCONSOLE_Output((void *) userConsole, "%s\n",
1000 userConsole->inputLine);
1002 /* Invoke console's enter-key callback function */
1003 if (command_split(userConsole->inputLine, &argc, argv,
1004 MAX_COMMAND_ARGS)) {
1005 OGLCONSOLE_Output((void *)userConsole,
1006 "Warning: Command line too long!\n");
1008 userConsole->enterKeyCallback((void *) userConsole,
1009 argc, argv);
1011 /* Erase command line */
1012 userConsole->inputCursorPos = 0;
1013 userConsole->inputLineLength = 0;
1014 userConsole->inputLine[0] = '\0';
1016 return 1;
1018 // Page up key
1019 else if (e->key.keysym.sym == SDLK_PAGEUP) {
1020 userConsole->lineScrollIndex -=
1021 userConsole->textHeight / 2;
1023 if (userConsole->lineScrollIndex < 0)
1024 userConsole->lineScrollIndex +=
1025 userConsole->maxLines;
1027 #if 0
1028 printf("scroll index = %i\n",
1029 userConsole->lineScrollIndex);
1030 #endif
1032 // Page down key
1033 else if (e->key.keysym.sym == SDLK_PAGEDOWN) {
1034 userConsole->lineScrollIndex +=
1035 userConsole->textHeight / 2;
1037 if (userConsole->lineScrollIndex >=
1038 userConsole->maxLines)
1039 userConsole->lineScrollIndex -=
1040 userConsole->maxLines;
1042 #if 0
1043 printf("scroll index = %i\n",
1044 userConsole->lineScrollIndex);
1045 #endif
1047 // Arrow key up
1048 else if (e->key.keysym.sym == KEY_UP) {
1049 // Shift key is for scrolling the output display
1050 if (e->key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) {
1051 if (--userConsole->lineScrollIndex < 0)
1052 userConsole->lineScrollIndex =
1053 userConsole->maxLines - 1;
1055 // No shift key is for scrolling through command history
1056 else {
1057 // -1 means we aren't look at history yet
1058 if (userConsole->historyScrollIndex == -1) {
1059 userConsole->historyScrollIndex =
1060 userConsole->historyQueueIndex;
1061 } else {
1062 // Wrap our history scrolling
1063 if (--userConsole->historyScrollIndex <
1065 userConsole->
1066 historyScrollIndex =
1067 MAX_HISTORY_COUNT;
1070 // If we've returned to our current position in the command
1071 // history, we'll just drop out of history mode
1072 if (userConsole->historyScrollIndex ==
1073 userConsole->historyQueueIndex + 1)
1074 userConsole->historyScrollIndex = -1;
1077 return 1;
1079 // Arrow key down
1080 else if (e->key.keysym.sym == KEY_DOWN) {
1081 // Shift key is for scrolling the output display
1082 if (e->key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) {
1083 if (++userConsole->lineScrollIndex >=
1084 userConsole->maxLines)
1085 userConsole->lineScrollIndex = 0;
1087 // No shift key is for scrolling through command history
1088 else {
1089 // -1 means we aren't look at history yet
1090 if (userConsole->historyScrollIndex != -1) {
1091 // Wrap our history scrolling
1092 if (++userConsole->historyScrollIndex >=
1093 MAX_HISTORY_COUNT)
1094 userConsole->
1095 historyScrollIndex = 0;
1097 // If we've returned to our current position in the command
1098 // history, we'll just drop out of history mode
1099 if (userConsole->historyScrollIndex ==
1100 userConsole->historyQueueIndex + 1)
1101 userConsole->
1102 historyScrollIndex = -1;
1103 } else {
1104 // TODO: be like, no bitch, there's no history down there
1107 return 1;
1109 // Arrow key left
1110 else if (e->key.keysym.sym == KEY_LEFT) {
1111 /* Yank the command history if necessary */
1112 OGLCONSOLE_YankHistory(userConsole);
1114 if (userConsole->inputCursorPos > 0)
1115 userConsole->inputCursorPos--;
1117 return 1;
1119 // Arrow key right
1120 else if (e->key.keysym.sym == KEY_RIGHT) {
1121 /* Yank the command history if necessary */
1122 OGLCONSOLE_YankHistory(userConsole);
1124 if (userConsole->inputCursorPos <
1125 userConsole->inputLineLength)
1126 userConsole->inputCursorPos++;
1128 return 1;
1132 return 0;
1134 #endif