2 * Command-line interface for the console
4 * Based on an early version of SDL_Console
5 * Written By: Garrett Banuk <mongoose@mongeese.org>
6 * Code Cleanup and heavily extended by: Clemens Wacha <reflex-2000@gmx.net>
7 * Ported to use native Descent interfaces by: Bradley Bell <btb@icculus.org>
9 * This is free, just be sure to give us credit when using it
10 * in any of your programs.
27 #define CLI_HISTORY_MAX 128
28 // Cut the buffer line if it becomes longer than this
29 #define CLI_CHARS_PER_LINE 128
30 // Cursor blink interval
31 #define CLI_BLINK_RATE (F1_0/2)
32 // Border in pixels from the most left to the first letter
33 #define CLI_CHAR_BORDER 4
34 // Default prompt used at the commandline
35 #define CLI_DEFAULT_PROMPT "]"
36 // Cursor shown if we are in insert mode
37 #define CLI_INS_CURSOR "_"
38 // Cursor shown if we are in overwrite mode
39 #define CLI_OVR_CURSOR "|"
41 int CLI_insert_mode; // Insert or Overwrite characters?
43 static char *CommandLines[CLI_HISTORY_MAX]; // List of all the past commands
44 static int TotalCommands; // Number of commands in the Back Commands
45 static char *Prompt; // Prompt displayed in command line
46 static char Command[CLI_CHARS_PER_LINE]; // current command in command line = lcommand + rcommand
47 static char LCommand[CLI_CHARS_PER_LINE]; // right hand side of cursor
48 static char RCommand[CLI_CHARS_PER_LINE]; // left hand side of cursor
49 static char VCommand[CLI_CHARS_PER_LINE]; // current visible command line
50 static int CursorPos; // Current cursor position in CurrentCommand
51 static int Offset; // CommandOffset (first visible char of command) - if command is too long to fit into console
52 static int CommandScrollBack; // How much the users scrolled back in the command lines
55 /* Frees all the memory loaded by the cli */
56 static void cli_free(void)
60 for (i = 0; i <= CLI_HISTORY_MAX - 1; i++) {
62 d_free(CommandLines[i]);
69 /* Initializes the cli */
75 CommandScrollBack = 0;
76 Prompt = d_strdup(CLI_DEFAULT_PROMPT);
78 memset(CommandLines, 0, sizeof(CommandLines));
79 memset(Command, 0, CLI_CHARS_PER_LINE);
80 memset(LCommand, 0, CLI_CHARS_PER_LINE);
81 memset(RCommand, 0, CLI_CHARS_PER_LINE);
82 memset(VCommand, 0, CLI_CHARS_PER_LINE);
88 /* Increments the command lines */
89 static void cli_newline(void)
93 if (CommandLines[CLI_HISTORY_MAX - 1])
94 d_free(CommandLines[CLI_HISTORY_MAX - 1]);
96 for (loop = CLI_HISTORY_MAX - 1; loop > 0; loop--)
97 CommandLines[loop] = CommandLines[loop - 1];
99 CommandLines[0] = NULL;
101 if (TotalCommands < CLI_HISTORY_MAX - 1)
106 /* Draws the command line the user is typing in to the screen */
107 /* completely rewritten by C.Wacha */
113 fix cur_time = timer_get_fixed_seconds();
114 static fix LastBlinkTime = 0; // Last time the cursor blinked
115 static int LastCursorPos = 0; // Last cursor position
116 static int Blink = 0; // Is the cursor currently blinking
118 // Concatenate the left and right side to command
119 strcpy(Command, LCommand);
120 strncat(Command, RCommand, CLI_CHARS_PER_LINE - strlen(Command));
122 gr_get_string_size(Command, &w, &h, &aw);
123 if (w > 0 && *Command)
124 real_aw = (float)w/(float)strlen(Command);
127 commandbuffer = (GWIDTH - 2*CLI_CHAR_BORDER)/real_aw - (int)strlen(Prompt) - 1; // -1 to make cursor visible
129 //calculate display offset from current cursor position
130 if (Offset < CursorPos - commandbuffer)
131 Offset = CursorPos - commandbuffer;
132 if(Offset > CursorPos)
135 // first add prompt to visible part
136 strcpy(VCommand, Prompt);
138 // then add the visible part of the command
139 strncat(VCommand, &Command[Offset], CLI_CHARS_PER_LINE - strlen(VCommand));
141 // now display the result
142 gr_string(CLI_CHAR_BORDER, y-h, VCommand);
144 // at last add the cursor
145 // check if the blink period is over
146 if (cur_time > LastBlinkTime) {
147 LastBlinkTime = cur_time + CLI_BLINK_RATE;
154 // check if cursor has moved - if yes display cursor anyway
155 if (CursorPos != LastCursorPos) {
156 LastCursorPos = CursorPos;
157 LastBlinkTime = cur_time + CLI_BLINK_RATE;
162 int prompt_width, cmd_width, h, w;
164 gr_get_string_size(Prompt, &prompt_width, &h, &w);
165 gr_get_string_size(LCommand + Offset, &cmd_width, &h, &w);
166 x = CLI_CHAR_BORDER + prompt_width + cmd_width;
168 gr_string(x, y-h, CLI_INS_CURSOR);
170 gr_string(x, y-h, CLI_OVR_CURSOR);
175 /* Executes the command entered */
176 void cli_execute(void)
178 if(strlen(Command) > 0) {
181 // copy the input into the past commands strings
182 CommandLines[0] = d_strdup(Command);
184 // display the command including the prompt
185 con_printf(CON_NORMAL, "%s%s\n", Prompt, Command);
190 CommandScrollBack = -1;
195 void cli_autocomplete(void)
200 command = cmd_complete(LCommand);
203 return; // no tab completion took place so return silently
205 j = (int)strlen(command);
206 if (j > CLI_CHARS_PER_LINE - 2)
207 j = CLI_CHARS_PER_LINE - 1;
209 memset(LCommand, 0, CLI_CHARS_PER_LINE);
212 for (i = 0; i < j; i++) {
214 LCommand[i] = command[i];
216 // add a trailing space
219 LCommand[j+1] = '\0';
223 void cli_cursor_left(void)
225 char temp[CLI_CHARS_PER_LINE];
229 strcpy(temp, RCommand);
230 strcpy(RCommand, &LCommand[strlen(LCommand)-1]);
231 strcat(RCommand, temp);
232 LCommand[strlen(LCommand)-1] = '\0';
237 void cli_cursor_right(void)
239 char temp[CLI_CHARS_PER_LINE];
241 if(CursorPos < strlen(Command)) {
243 strncat(LCommand, RCommand, 1);
244 strcpy(temp, RCommand);
245 strcpy(RCommand, &temp[1]);
250 void cli_cursor_home(void)
252 char temp[CLI_CHARS_PER_LINE];
255 strcpy(temp, RCommand);
256 strcpy(RCommand, LCommand);
257 strncat(RCommand, temp, strlen(temp));
258 memset(LCommand, 0, CLI_CHARS_PER_LINE);
262 void cli_cursor_end(void)
264 CursorPos = (int)strlen(Command);
265 strncat(LCommand, RCommand, strlen(RCommand));
266 memset(RCommand, 0, CLI_CHARS_PER_LINE);
270 void cli_cursor_del(void)
272 char temp[CLI_CHARS_PER_LINE];
274 if (strlen(RCommand) > 0) {
275 strcpy(temp, RCommand);
276 strcpy(RCommand, &temp[1]);
281 void cli_cursor_backspace(void)
288 LCommand[strlen(LCommand)-1] = '\0';
293 void cli_add_character(char character)
295 if (strlen(Command) < CLI_CHARS_PER_LINE - 1)
298 LCommand[strlen(LCommand)] = character;
299 LCommand[strlen(LCommand)] = '\0';
301 if (!CLI_insert_mode)
309 memset(Command, 0, CLI_CHARS_PER_LINE);
310 memset(LCommand, 0, CLI_CHARS_PER_LINE);
311 memset(RCommand, 0, CLI_CHARS_PER_LINE);
312 memset(VCommand, 0, CLI_CHARS_PER_LINE);
316 void cli_history_prev(void)
318 if(CommandScrollBack < TotalCommands - 1) {
319 /* move back a line in the command strings and copy the command to the current input string */
321 memset(RCommand, 0, CLI_CHARS_PER_LINE);
323 strcpy(LCommand, CommandLines[CommandScrollBack]);
324 CursorPos = (int)strlen(CommandLines[CommandScrollBack]);
329 void cli_history_next(void)
331 if(CommandScrollBack > -1) {
332 /* move forward a line in the command strings and copy the command to the current input string */
334 memset(RCommand, 0, CLI_CHARS_PER_LINE);
335 memset(LCommand, 0, CLI_CHARS_PER_LINE);
337 if(CommandScrollBack > -1)
338 strcpy(LCommand, CommandLines[CommandScrollBack]);
339 CursorPos = (int)strlen(LCommand);