Reinitialize console text / font on resolution change
[attac-man.git] / oglconsole.c
blob055ec8a81d455e2cb1ffffb6eefec7652b1c2b89
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>
22 #define C ((_OGLCONSOLE_Console*)console)
24 /* OGLCONSOLE font */
25 #include "ConsoleFont.c"
26 #define FIRST_CHARACTER ' '
27 #define LAST_CHARACTER '~'
29 #define CHAR_PIXEL_W 6
30 #define CHAR_PIXEL_H 13
31 #define CHAR_WIDTH 0.0234375 /* ogl tex coords */
32 #define CHAR_HEIGHT 0.203125 /* ogl tex coords */
34 /* This is how long the animation should take to make the transition between
35 * "hidden" and "visible" console visibility modes (expressed in milliseconds) */
36 #define SLIDE_MS 700
38 /* If we don't know how to retrieve the time then we can just use a number of
39 * frames to divide up the time it takes to transition between "hidden" and
40 * "visible" console visibility modes */
41 #define SLIDE_STEPS 25
43 GLuint OGLCONSOLE_glFontHandle = 0;
44 int
45 OGLCONSOLE_CreateFont(void)
48 int err = glGetError();
49 if (err)
50 printf("GL ERROR: %i\n", err);
52 #ifdef DEBUG
53 puts("Creating OGLCONSOLE font");
54 #endif
56 /* Get a font index from OpenGL */
57 glGenTextures(1, &OGLCONSOLE_glFontHandle);
59 int err = glGetError();
60 if (err)
61 printf("glGenTextures() error: %i\n", err);
64 /* Select our font */
65 glBindTexture(GL_TEXTURE_2D, OGLCONSOLE_glFontHandle);
67 int err = glGetError();
68 if (err)
69 printf("glBindTexture() error: %i\n", err);
72 /* Set some parameters i guess */
73 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
74 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
76 /* Upload our font */
77 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
78 OGLCONSOLE_FontData.width, OGLCONSOLE_FontData.height, 0,
79 GL_RGB, GL_UNSIGNED_BYTE, OGLCONSOLE_FontData.pixel_data);
82 int err = glGetError();
83 if (err)
84 printf("glTexImage2D() error: %i\n", err);
87 #ifdef DEBUG
88 puts("Created OGLCONSOLE font");
89 #endif
90 return 1;
93 /* TODO: Expose these macros to the user? */
95 /* This is the longest command line that the user can enter TODO: Make dynamic
96 * so that the user can enter any length line */
97 #define MAX_INPUT_LENGTH 256
99 /* This is the number of command line entries that the console will remember (so
100 * that the user can use the up/down keys to see and easily re-execute his past
101 * commands) */
102 #define MAX_HISTORY_COUNT 25
104 /* This is the default number of lines for the console to remember (that is to
105 * say, the user can scroll up and down to see what has been printed to the
106 * console in the past, and this is the number of those lines, plus the number
107 * of lines shown on the screen at all times) */
108 #define DEFAULT_MAX_LINES 100
110 /* OGLCONSOLE console structure */
111 typedef struct {
112 GLdouble mvMatrix[16];
113 int mvMatrixUse;
115 GLdouble pMatrix[16];
116 int pMatrixUse;
118 /* Screen+scrollback lines (console output) */
119 char *lines;
120 int lines_size;
121 int maxLines, lineQueueIndex, lineScrollIndex;
123 /* History scrollback (command input) */
124 char history[MAX_HISTORY_COUNT][MAX_INPUT_LENGTH];
125 int historyQueueIndex, historyScrollIndex;
127 /* Current input line */
128 char inputLine[MAX_INPUT_LENGTH];
129 int inputCursorPos, inputLineLength;
131 /* Rows and columns of text to display */
132 int textWidth, textHeight;
134 /* Current column of text where new text will be inserted */
135 char *outputCursor;
136 int outputNewline;
138 /* Width and height of a single character for the GL */
139 GLdouble characterWidth, characterHeight;
141 /* Basic options */
142 int visibility;
144 /* Various callback functions defined by the user */
145 void (*enterKeyCallback) (OGLCONSOLE_Console console, char *cmd);
147 } _OGLCONSOLE_Console;
149 /* To save code, I've gone with an imperative "modal" kind of interface */
150 _OGLCONSOLE_Console *programConsole = NULL;
152 /* This console is the console currently receiving user input */
153 _OGLCONSOLE_Console *userConsole = NULL;
155 /* Set the callback for a console */
156 void
157 OGLCONSOLE_EnterKey(void (*cbfun) (OGLCONSOLE_Console console, char *cmd))
159 programConsole->enterKeyCallback = cbfun;
162 void
163 OGLCONSOLE_DefaultEnterKeyCallback(OGLCONSOLE_Console console, char __attribute__((unused)) *cmd)
165 OGLCONSOLE_Output(console,
166 "No enter key callback is registered for this console!\n");
170 void
171 OGLCONSOLE_InitText(void *c, int res_x, int res_y)
173 _OGLCONSOLE_Console *console = c;
174 char *new_lines;
175 int new_lines_size;
177 if (!console) {
178 console = userConsole;
179 if (!userConsole)
180 return;
183 /* Textual dimensions */
185 console->textWidth = res_x / CHAR_PIXEL_W;
186 console->textHeight = res_y / CHAR_PIXEL_H;
187 console->characterWidth = 1.0 / console->textWidth;
188 console->characterHeight = 1.0 / console->textHeight;
190 /* Different values have different meanings for xMatrixUse:
191 0) Do not change the matrix before rendering
192 1) Upload the console's matrix before rendering
193 2) Multiply the console's matrix before rendering */
195 /* Initialize its projection matrix */
196 console->pMatrixUse = 1;
197 glMatrixMode(GL_PROJECTION);
198 glPushMatrix();
199 glOrtho(0, console->textWidth, 0, console->textHeight, -1, 1);
200 glGetDoublev(GL_MODELVIEW_MATRIX, console->pMatrix);
201 glPopMatrix();
203 /* Initialize its modelview matrix */
204 console->mvMatrixUse = 1;
205 glMatrixMode(GL_MODELVIEW);
206 glPushMatrix();
207 glLoadIdentity();
208 glTranslated(-1, -1, 0);
209 glScaled(2, 2, 1);
210 glGetDoublev(GL_MODELVIEW_MATRIX, console->mvMatrix);
211 glPopMatrix();
213 /* Screen and scrollback lines */
214 /* This is the total number of screen lines in memory (start blank) */
215 console->maxLines = DEFAULT_MAX_LINES;
217 /* (re)allocate space for text */
218 new_lines_size = console->maxLines * console->textWidth + 1;
219 new_lines = malloc(new_lines_size);
220 /* XXX return value */
221 memset(new_lines, 0, new_lines_size);
223 if (console->lines) {
224 #if 0
225 if (new_lines_size >= console->lines_size) {
226 memcpy(new_lines, console->lines, console->lines_size);
227 } else {
228 memcpy(new_lines,
229 console->lines + (console->lines_size-new_lines_size),
230 new_lines_size);
232 #endif
234 free(console->lines);
237 console->lines_size = new_lines_size;
238 console->lines = new_lines;
240 /* This cursor points to the X pos where console output is next destined */
241 console->outputCursor = console->lines;
243 /* This variable represents whether or not a newline has been left */
244 console->outputNewline = 0;
245 /* This cursor points to the X pos where console output is next destined */
246 console->outputCursor = console->lines;
247 /* This cursor points to what line console output is next destined for */
248 console->lineQueueIndex = 0;
249 /* This cursor points to what line the console view is scrolled to */
250 console->lineScrollIndex = console->maxLines - console->textHeight + 1;
252 /* Initialize the user's input (command line) */
253 console->inputLineLength = 0;
254 console->inputCursorPos = 0;
255 console->inputLine[0] = '\0';
257 OGLCONSOLE_Output((void *) console,
258 "Console Resolution: %i x %i\n"
259 "Text Size: %i x %i\n",
260 res_x, res_y,
261 console->textWidth, console->textHeight);
264 OGLCONSOLE_Console
265 OGLCONSOLE_Create()
267 _OGLCONSOLE_Console *console;
268 GLint viewport[4];
270 /* If font hasn't been created, we create it */
271 if (!glIsTexture(OGLCONSOLE_glFontHandle))
272 OGLCONSOLE_CreateFont();
274 /* Allocate memory for our console */
275 console = (void *) malloc(sizeof (_OGLCONSOLE_Console));
276 memset(console, 0, sizeof(*console));
278 console->maxLines = DEFAULT_MAX_LINES;
280 glGetIntegerv(GL_VIEWPORT, viewport);
281 OGLCONSOLE_InitText(console, viewport[2], viewport[3]);
283 /* Callbacks */
284 console->enterKeyCallback = OGLCONSOLE_DefaultEnterKeyCallback;
286 /* The console starts life invisible */
287 console->visibility = 0;
289 /* If no consoles existed before, we select this one for convenience */
290 if (!programConsole)
291 programConsole = console;
292 if (!userConsole)
293 userConsole = console;
295 /* Temporary shit */
296 OGLCONSOLE_Output((void *) console,
297 "Console input length: %i\n", MAX_INPUT_LENGTH);
298 OGLCONSOLE_Output((void *) console, "Console initialized\n");
300 /* Return the opaque pointer to the programmer */
301 return (OGLCONSOLE_Console) console;
304 /* This functoin is only used internally; the user ultimately invokes this
305 * function through either a call to Destroy() or Quit(); the purpose of this
306 * mechanism is to warn the user if he has explicitly destroyed a console that
307 * was engaged in operation at the time they destroyed it (the only two
308 * operations a console can be engaged in are receiving programmer interaction,
309 * or receiving end-user interaction. fyi, "user" always refers to the
310 * programmer, end-user refers to the real end-user) */
311 void
312 OGLCONSOLE_DestroyReal(OGLCONSOLE_Console console, int warn)
314 free(C);
316 if (warn) {
317 if (programConsole == C) {
318 fprintf(stderr,
319 "Warning: OGLCONSOLE you just destroyed the programConsole!\n");
320 programConsole = NULL;
323 if (userConsole == C) {
324 fprintf(stderr,
325 "Warning: OGLCONSOLE you just destroyed the userConsole!\n");
326 userConsole = NULL;
331 /* The user can call this function to free a console. A warning is printed to
332 * stderr if the user has destroyed a console destroyed while it was receiving
333 * input, see DestroyReal() for details TODO: there are currently no semantics
334 * under consideration for explicitly destroying an 'engaged' console WITHOUT
335 * having a warning issued, nor is there a way to deselect a console without
336 * also selecting a new one as of yet */
337 void
338 OGLCONSOLE_Destroy(OGLCONSOLE_Console console)
340 OGLCONSOLE_DestroyReal(console, 1);
343 /* This function frees all of the consoles that the library is actively aware
344 * of, and is intended to be used by programs that have only a single console;
345 * technically this particular function will free up to two consoles, but don't
346 * count on it because that may change; no warnings are issued by this function */
347 void
348 OGLCONSOLE_Quit()
350 OGLCONSOLE_DestroyReal((void *) programConsole, 0);
351 if (programConsole != userConsole)
352 OGLCONSOLE_DestroyReal((void *) userConsole, 0);
353 programConsole = NULL;
354 userConsole = NULL;
357 /* Select a console to receive input from the user (read: programmer); this
358 * function is intended for use by applications which create MORE THAN ONE
359 * console, since every newly created console is automatically selected for
360 * programmer input ('programmer input' refers to calling any function which
361 * does not specify a console explicitly in its parameter list. the console
362 * which is 'engaged' by 'programmer input' at the time of calling one of these
363 * functions is the console that function will operate on */
364 void
365 OGLCONSOLE_EditConsole(OGLCONSOLE_Console console)
367 programConsole = C;
370 /* Show or hide a console */
371 void
372 OGLCONSOLE_SetVisibility(int __attribute__ ((unused)) visible)
376 /* Get current configuration information about a console */
377 void
378 OGLCONSOLE_Info(void)
380 puts("TODO: print some console info here");
383 /* This routine is meant for applications with a single console, if you use
384 * multiple consoles in your program, use Render() instead */
385 void
386 OGLCONSOLE_Draw()
388 OGLCONSOLE_Render((void *) userConsole);
391 /* Internal functions for drawing text. You don't want these, do you? */
392 void OGLCONSOLE_DrawString(char *s, double x, double y, double w, double h,
393 double z);
394 void OGLCONSOLE_DrawWrapString(char *s, double x, double y, double w, double h,
395 double z, int wrap);
396 void OGLCONSOLE_DrawCharacter(int c, double x, double y, double w, double h,
397 double z);
399 /* This function draws a single specific console; if you only use one console in
400 * your program, use Draw() instead */
401 void
402 OGLCONSOLE_Render(OGLCONSOLE_Console console)
404 /* Don't render hidden console */
405 if (C->visibility == 0)
406 return;
408 glMatrixMode(GL_PROJECTION);
409 glPushMatrix();
410 glLoadMatrixd(C->pMatrix);
412 glMatrixMode(GL_MODELVIEW);
413 glPushMatrix();
414 glLoadMatrixd(C->mvMatrix);
416 glPushAttrib(GL_ALL_ATTRIB_BITS);
418 /* glEnable(GL_BLEND);
419 glBlendFunc(GL_ONE, GL_ONE);*/
421 /* TODO: This SHOULD become an option at some point because the
422 * infrastructure for "real" consoles in the game (like you could walk up to
423 * a computer terminal and manipulate a console on a computer using
424 * oglconsole) already exists; you'd want depth testing in that case */
425 glDisable(GL_DEPTH_TEST);
427 /* Render hiding / showing console in a special manner. Zero means hidden. 1
428 * means visible. All other values are traveling toward zero or one. TODO:
429 * Make this time dependent */
430 if (C->visibility != 1) {
431 double d; /* bra size */
432 int v = C->visibility;
434 /* Count down in both directions */
435 if (v < 0) {
436 v ^= -1;
437 C->visibility++;
438 } else {
439 v = SLIDE_STEPS - v;
440 C->visibility--;
443 d = 0.04 * v;
444 glTranslated(0, 1 - d, 0);
447 /* First we draw our console's background TODO: Add something fancy? */
448 glDisable(GL_TEXTURE_2D);
449 glEnable(GL_BLEND);
451 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
452 // glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
453 glColor4d(.1, 0, 0, 0.5);
455 glBegin(GL_QUADS);
456 glVertex3d(0, 0, 0);
457 glVertex3d(1, 0, 0);
458 glVertex3d(1, 1, 0);
459 glVertex3d(0, 1, 0);
460 glEnd();
462 // Change blend mode for drawing text
463 glBlendFunc(GL_ONE, GL_ONE);
465 /* Select the console font */
466 glEnable(GL_TEXTURE_2D);
467 glBindTexture(GL_TEXTURE_2D, OGLCONSOLE_glFontHandle);
469 /* Recolor text */
470 glColor3d(0, 1, 0);
472 /* Render console contents */
473 glBegin(GL_QUADS);
475 /* Graphical line, and line in lines[] */
476 int gLine, tLine = C->lineScrollIndex;
478 /* Iterate through each line being displayed */
479 for (gLine = 0; gLine < C->textHeight; gLine++) {
480 /* Draw this line of text adjusting for user scrolling up/down */
481 OGLCONSOLE_DrawString(C->lines + (tLine * C->textWidth),
483 (C->textHeight -
484 gLine) * C->characterHeight,
485 C->characterWidth,
486 C->characterHeight, 0);
488 /* Grab next line of text using wheel-queue wrapping */
489 if (++tLine >= C->maxLines)
490 tLine = 0;
493 /* Here we draw the current commandline, it will either be a line from
494 * the command history or the line being edited atm */
495 if (C->historyScrollIndex >= 0) {
496 glColor3d(1, 0, 0);
497 OGLCONSOLE_DrawString(C->history[C->historyScrollIndex],
498 0, 0,
499 C->characterWidth,
500 C->characterHeight, 0);
501 } else {
502 /* Draw input line cyan */
503 glColor3d(0, 1, 1);
504 OGLCONSOLE_DrawString(C->inputLine,
505 0, 0,
506 C->characterWidth,
507 C->characterHeight, 0);
509 /* Draw cursor beige */
510 glColor3d(1, 1, .5);
511 OGLCONSOLE_DrawCharacter('_' - ' ',
512 C->inputCursorPos *
513 C->characterWidth, 0,
514 C->characterWidth,
515 C->characterHeight, 0);
518 glEnd();
520 /* Relinquish our rendering settings */
521 glMatrixMode(GL_PROJECTION);
522 glPopMatrix();
524 glMatrixMode(GL_MODELVIEW);
525 glPopMatrix();
527 glPopAttrib();
530 /* Issue rendering commands for a single a string */
531 void
532 OGLCONSOLE_DrawString(char *s, double x, double y, double w, double h, double z)
534 while (*s) {
535 OGLCONSOLE_DrawCharacter(*s - ' ', x, y, w, h, z);
536 s++;
537 x += w;
541 /* Issue rendering commands for a single a string */
542 void
543 OGLCONSOLE_DrawWrapString(char *s, double x, double y, double w, double h,
544 double z, int wrap)
546 int pos = 0;
547 double X = x;
549 while (*s) {
550 OGLCONSOLE_DrawCharacter(*s - ' ', X, y, w, h, z);
551 s++;
552 X += w;
554 if (++pos >= wrap) {
555 pos = 0;
556 y += h;
557 X = x;
562 /* Issue rendering commands for a single character */
563 void
564 OGLCONSOLE_DrawCharacter(int c, double x, double y, double w, double h,
565 double z)
567 // static int message = 0;
568 double cx, cy, cX, cY;
570 // if (c < FIRST_CHARACTER || c > LAST_CHARACTER)
571 // c = (c - FIRST_CHARACTER) % (LAST_CHARACTER - FIRST_CHARACTER);
572 // else c -= FIRST_CHARACTER;
574 cx = (c % 42) * CHAR_WIDTH;
575 cy = 1.0 - (c / 42) * CHAR_HEIGHT;
576 cX = cx + CHAR_WIDTH;
577 cY = 1.0 - (c / 42 + 1) * CHAR_HEIGHT;
579 /* if (message != c)
581 printf("For %i we got %f, %f\n", c, x, y);
582 message = c;
585 /* This should occur outside of this function for optimiation TODO: MOVE IT */
586 glTexCoord2d(cx, cy);
587 glVertex3d(x, y, z);
588 glTexCoord2d(cX, cy);
589 glVertex3d(x + w, y, z);
590 glTexCoord2d(cX, cY);
591 glVertex3d(x + w, y + h, z);
592 glTexCoord2d(cx, cY);
593 glVertex3d(x, y + h, z);
596 /* This is the final, internal function for printing text to a console */
597 void
598 OGLCONSOLE_Output(OGLCONSOLE_Console console, const char *s, ...)
600 va_list argument;
602 /* cache some console properties */
603 int lineQueueIndex = C->lineQueueIndex;
604 int lineScrollIndex = C->lineScrollIndex;
605 int textWidth = C->textWidth;
606 int maxLines = C->maxLines;
608 /* String buffer */
609 char output[4096];
611 /* string copy cursors */
612 char *consoleCursor, *outputCursor = output;
614 /* Acrue arguments in argument list */
615 va_start(argument, s);
616 vsnprintf(output, 4096, s, argument);
617 va_end(argument);
619 /* This cursor tells us where in the console display we are currently
620 * copying text into from the "output" string */
621 consoleCursor = C->outputCursor;
623 while (*outputCursor) {
624 /* Here we check to see if any conditions require console line
625 * advancement. These two conditions are:
626 1) Hitting the end of the screen
627 2) Getting a newlien character */
628 if ((C->outputNewline) ||
629 (consoleCursor - (C->lines + lineQueueIndex * textWidth))
630 >= (textWidth - 1)) {
631 C->outputNewline = 0;
633 //puts("incrementing to the next line");
635 /* Inrement text-line index, with wrapping */
636 if (++lineQueueIndex >= maxLines)
637 lineQueueIndex = 0;
639 /* Scroll the console display one line TODO: Don't scroll if the console is
640 * currently scrolled away from the end of output? */
641 if (++lineScrollIndex >= maxLines)
642 lineScrollIndex = 0;
644 /* Reposition the cursor at the beginning of the new line */
645 consoleCursor =
646 C->lines + lineQueueIndex * C->textWidth;
649 /* If we encounter a newline character, we set the newline flag, which
650 * tells the console to advance one line before it prints the next
651 * character. The reason we do it this way is to defer line-advancement,
652 * and thus we needn't suffer through a needless blank line between
653 * console output and the command line, wasting precious screen
654 * real-estate */
655 if (*outputCursor == '\n') {
656 C->outputNewline = 1;
657 outputCursor++;
658 continue;
661 /* copy a single character */
662 *(consoleCursor++) = *(outputCursor++);
665 /* Unless we're at the very end of our current line, we finish up by capping
666 * a NULL terminator on the current line */
667 if (consoleCursor != C->lines + (lineQueueIndex + 1) * C->textWidth - 1)
668 *consoleCursor = '\0';
670 /* Restore cached values */
671 C->lineQueueIndex = lineQueueIndex;
672 C->lineScrollIndex = lineScrollIndex;
673 C->outputCursor = consoleCursor; // TODO: confusing variable names
675 /* old way of copying the text into the console */
676 //strcpy(C->lines[C->lineQueueIndex], output);
677 #ifdef DEBUG
678 printf("Copied \"%s\" into line %i\n", output, C->lineQueueIndex);
679 #endif
682 /* Mono-Console Users: print text to the console; multi-console users use
683 * Output() */
684 void
685 OGLCONSOLE_Print(char *s, ...)
687 va_list argument;
688 char output[4096];
690 /* Acrue arguments in argument list */
691 va_start(argument, s);
692 vsnprintf(output, 4096, s, argument);
693 va_end(argument);
695 /* TODO: Find some way to pass the va_list arguments to OGLCONSOLE_Output
696 * so that we don't waste extra time with the "%s" bullshit */
697 OGLCONSOLE_Output((OGLCONSOLE_Console) userConsole, "%s", output);
700 #if 0
701 /* Multi-Console Users: print text to a specific console; mono-console users use
702 * Print() */
703 void
704 OGLCONSOLE_Output(OGLCONSOLE_Console console, char *s)
706 /* TODO: Chop up output to wrap text */
708 /* TODO: Add auto-scroll (the commented out code here is a failed attempt,
709 * my brain is too scattered to do it) */
710 /* If the console isn't scrolled up, then we move the scroll point */
711 /* if (C->lineQueueIndex - C->lineScrollIndex == C->textHeight)
712 if (++C->lineScrollIndex - C->lineScrollIndex >= MAX_LINE_COUNT)
713 C->lineScrollIndex = 0;*/
716 #endif
718 /* Internal encapsulation of the act for adding a command the user executed to
719 * their command history for that particular console */
720 void
721 OGLCONSOLE_AddHistory(_OGLCONSOLE_Console * console, char *s)
723 if (++C->historyQueueIndex > MAX_HISTORY_COUNT)
724 C->historyQueueIndex = 0;
726 strcpy(C->history[C->historyQueueIndex], s);
729 void
730 OGLCONSOLE_YankHistory(_OGLCONSOLE_Console * console)
732 /* First we have to see if we are browsing our command history */
733 if (console->historyScrollIndex != -1) {
734 /* Copy the selected command into the current input line */
735 strcpy(console->inputLine,
736 console->history[console->historyScrollIndex]);
738 /* Set up this shite */
739 console->inputCursorPos =
740 console->inputLineLength = strlen(console->inputLine);
742 /* Drop out of history browsing mode */
743 console->historyScrollIndex = -1;
747 #ifndef OGLCONSOLE_USE_SDL
748 #error **************************************************************************
749 #error
750 #error
751 #error
752 #error Only SDL is supported so far: you must define the OGLCONSOLE_USE_SDL macro
753 #error
754 #error
755 #error
756 #error **************************************************************************
757 #endif
759 /* This function tries to handle the incoming SDL event. In the future there may
760 * be non-SDL analogs for input systems such as GLUT. Returns true if the event
761 * was handled by the console. If console is hidden, no events are handled. */
762 #ifdef OGLCONSOLE_USE_SDL
763 #include "SDL.h"
764 #define KEY_BACKSPACE SDLK_BACKSPACE
765 #define KEY_DELETE SDLK_DELETE
766 #define KEY_RETURN SDLK_RETURN
767 #define KEY_UP SDLK_UP
768 #define KEY_DOWN SDLK_DOWN
769 #define KEY_LEFT SDLK_LEFT
770 #define KEY_RIGHT SDLK_RIGHT
771 #define KEY_PAGEUP SDLK_PAGEUP
772 #define KEY_PAGEDOWN SDLK_PAGEDOWN
773 #define KMOD_CAPITALIZE (KMOD_LSHIFT|KMOD_RSHIFT|KMOD_CAPS)
775 OGLCONSOLE_SDLEvent(SDL_Event * e)
777 /* If the terminal is hidden we only check for show/hide key */
778 if (userConsole->visibility < 1) {
779 if (e->type == SDL_KEYDOWN && e->key.keysym.sym == '`') {
780 // TODO: Fetch values from OS?
781 // TODO: Expose them to the program
782 SDL_EnableKeyRepeat(250, 30);
783 userConsole->visibility += SLIDE_STEPS;
784 return 1;
787 return 0;
790 /* TODO: SDL_KEYPRESS? ENABLE KEY REPEAT USING THE HIDE/SHOW FUNCTION? */
791 if (e->type == SDL_KEYDOWN) {
792 /* Reject most modifier keys TODO: Add some accelerator keys? */
793 if (e->key.keysym.mod & ~(KMOD_CAPITALIZE | KMOD_NUM))
794 return 0;
796 /* Check for hide key */
797 if (e->key.keysym.sym == '`' || e->key.keysym.sym == SDLK_ESCAPE) {
798 /* Tell console to slide into closing */
799 userConsole->visibility -= SLIDE_STEPS;
801 /* Disable key repeat */
802 SDL_EnableKeyRepeat(0, 0);
803 return 1;
806 /* TODO: Find out how to handle CAPSLOCK */
807 if (e->key.keysym.sym >= ' ' && e->key.keysym.sym <= '~') {
808 int k = e->key.keysym.sym;
809 char *c, *d;
811 /* Yank the command history if necessary */
812 OGLCONSOLE_YankHistory(userConsole);
814 /* Capitalize if necessary */
815 if (e->key.keysym.mod & KMOD_CAPITALIZE) {
816 static
817 const int capital[] =
818 { (int) ' ', (int) '!', (int) '"',
819 (int) '#',
820 (int) '$', (int) '%', (int) '&',
821 (int) '"', (int) '(', (int) ')',
822 (int) '*', (int) '+', (int) '<',
823 (int) '_', (int) '>', (int) '?',
824 (int) ')', (int) '!', (int) '@',
825 (int) '#', (int) '$', (int) '%',
826 (int) '^', (int) '&', (int) '*',
827 (int) '(', (int) ':', (int) ':',
828 (int) '<', (int) '+', (int) '>',
829 (int) '?', (int) '@', (int) 'A',
830 (int) 'B', (int) 'C', (int) 'D',
831 (int) 'E', (int) 'F', (int) 'G',
832 (int) 'H', (int) 'I', (int) 'J',
833 (int) 'K', (int) 'L', (int) 'M',
834 (int) 'N', (int) 'O', (int) 'P',
835 (int) 'Q', (int) 'R', (int) 'S',
836 (int) 'T', (int) 'U', (int) 'V',
837 (int) 'W', (int) 'X', (int) 'Y',
838 (int) 'Z', (int) '{', (int) '|',
839 (int) '}', (int) '^', (int) '_',
840 (int) '~', (int) 'A', (int) 'B',
841 (int) 'C', (int) 'D', (int) 'E',
842 (int) 'F', (int) 'G', (int) 'H',
843 (int) 'I', (int) 'J', (int) 'K',
844 (int) 'L', (int) 'M', (int) 'N',
845 (int) 'O', (int) 'P', (int) 'Q',
846 (int) 'R', (int) 'S', (int) 'T',
847 (int) 'U', (int) 'V', (int) 'W',
848 (int) 'X', (int) 'Y', (int) 'Z',
849 (int) '{', (int) '|', (int) '}',
850 (int) '~'
853 /* If we're not explicitly holding a shift key, that means just
854 * capslock, which means we only capitalize letters */
855 if ((k >= 'a' && k <= 'z')
856 || (e->key.keysym.mod & KMOD_SHIFT))
857 k = capital[k - ' '];
860 /* Point to the cursor position and the end of the string */
861 c = userConsole->inputLine +
862 userConsole->inputCursorPos;
863 d = userConsole->inputLine +
864 userConsole->inputLineLength + 1;
866 /* Slide some of the string to the right */
867 for (; d != c; d--)
868 *d = *(d - 1);
870 /* Insert new character */
871 *c = k;
873 /* Increment input line length counter */
874 userConsole->inputLineLength++;
876 /* Advance input cursor position */
877 userConsole->inputCursorPos++;
879 return 1;
882 else if (e->key.keysym.sym == KEY_DELETE
883 || e->key.keysym.sym == KEY_BACKSPACE) {
884 /* Yank the command history if necessary */
885 OGLCONSOLE_YankHistory(userConsole);
887 /* If string is not empty */
888 if (userConsole->inputLineLength) {
889 char *end, *c;
891 /* Backspace is like pressing LEFT and then DELETE */
892 if (e->key.keysym.sym == KEY_BACKSPACE)
893 userConsole->inputCursorPos--;
895 /* With delete we much check more than just inputLineLength */
896 else if (userConsole->inputCursorPos ==
897 userConsole->inputLineLength)
898 return 1;
900 c = userConsole->inputLine +
901 userConsole->inputCursorPos;
902 end =
903 userConsole->inputLine +
904 --userConsole->inputLineLength;
906 while (c <= end) {
907 *c = *(c + 1);
908 c++;
912 return 1;
915 else if (e->key.keysym.sym == KEY_RETURN) {
916 /* Yank the command history if necessary */
917 OGLCONSOLE_YankHistory(userConsole);
919 /* Add user's command to history */
920 OGLCONSOLE_AddHistory(userConsole,
921 userConsole->inputLine);
923 /* Print user's command to the console */
924 OGLCONSOLE_Output((void *) userConsole, "%s\n",
925 userConsole->inputLine);
927 /* Invoke console's enter-key callback function */
928 userConsole->enterKeyCallback((void *) userConsole,
929 userConsole->inputLine);
931 /* Erase command line */
932 userConsole->inputCursorPos = 0;
933 userConsole->inputLineLength = 0;
934 userConsole->inputLine[0] = '\0';
936 return 1;
938 // Page up key
939 else if (e->key.keysym.sym == SDLK_PAGEUP) {
940 userConsole->lineScrollIndex -=
941 userConsole->textHeight / 2;
943 if (userConsole->lineScrollIndex < 0)
944 userConsole->lineScrollIndex +=
945 userConsole->maxLines;
947 #if 0
948 printf("scroll index = %i\n",
949 userConsole->lineScrollIndex);
950 #endif
952 // Page down key
953 else if (e->key.keysym.sym == SDLK_PAGEDOWN) {
954 userConsole->lineScrollIndex +=
955 userConsole->textHeight / 2;
957 if (userConsole->lineScrollIndex >=
958 userConsole->maxLines)
959 userConsole->lineScrollIndex -=
960 userConsole->maxLines;
962 #if 0
963 printf("scroll index = %i\n",
964 userConsole->lineScrollIndex);
965 #endif
967 // Arrow key up
968 else if (e->key.keysym.sym == KEY_UP) {
969 // Shift key is for scrolling the output display
970 if (e->key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) {
971 if (--userConsole->lineScrollIndex < 0)
972 userConsole->lineScrollIndex =
973 userConsole->maxLines - 1;
975 // No shift key is for scrolling through command history
976 else {
977 // -1 means we aren't look at history yet
978 if (userConsole->historyScrollIndex == -1) {
979 userConsole->historyScrollIndex =
980 userConsole->historyQueueIndex;
981 } else {
982 // Wrap our history scrolling
983 if (--userConsole->historyScrollIndex <
985 userConsole->
986 historyScrollIndex =
987 MAX_HISTORY_COUNT;
990 // If we've returned to our current position in the command
991 // history, we'll just drop out of history mode
992 if (userConsole->historyScrollIndex ==
993 userConsole->historyQueueIndex + 1)
994 userConsole->historyScrollIndex = -1;
997 return 1;
999 // Arrow key down
1000 else if (e->key.keysym.sym == KEY_DOWN) {
1001 // Shift key is for scrolling the output display
1002 if (e->key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) {
1003 if (++userConsole->lineScrollIndex >=
1004 userConsole->maxLines)
1005 userConsole->lineScrollIndex = 0;
1007 // No shift key is for scrolling through command history
1008 else {
1009 // -1 means we aren't look at history yet
1010 if (userConsole->historyScrollIndex != -1) {
1011 // Wrap our history scrolling
1012 if (++userConsole->historyScrollIndex >=
1013 MAX_HISTORY_COUNT)
1014 userConsole->
1015 historyScrollIndex = 0;
1017 // If we've returned to our current position in the command
1018 // history, we'll just drop out of history mode
1019 if (userConsole->historyScrollIndex ==
1020 userConsole->historyQueueIndex + 1)
1021 userConsole->
1022 historyScrollIndex = -1;
1023 } else {
1024 // TODO: be like, no bitch, there's no history down there
1027 return 1;
1029 // Arrow key left
1030 else if (e->key.keysym.sym == KEY_LEFT) {
1031 /* Yank the command history if necessary */
1032 OGLCONSOLE_YankHistory(userConsole);
1034 if (userConsole->inputCursorPos > 0)
1035 userConsole->inputCursorPos--;
1037 return 1;
1039 // Arrow key right
1040 else if (e->key.keysym.sym == KEY_RIGHT) {
1041 /* Yank the command history if necessary */
1042 OGLCONSOLE_YankHistory(userConsole);
1044 if (userConsole->inputCursorPos <
1045 userConsole->inputLineLength)
1046 userConsole->inputCursorPos++;
1048 return 1;
1052 return 0;
1054 #endif