]> icculus.org git repositories - btb/d2x.git/blob - main/cli.c
keep movies out of the hog namespace
[btb/d2x.git] / main / cli.c
1 /*
2  *  Command-line interface for the console
3  *
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>
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 <string.h>
18
19 #include "inferno.h"
20 #include "maths.h"
21 #include "gr.h"
22 #include "timer.h"
23 #include "u_mem.h"
24 #include "strutil.h"
25
26
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          "|"
40
41 int CLI_insert_mode;                        // Insert or Overwrite characters?
42
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 size_t CursorPos;                    // Current cursor position in CurrentCommand
51 static size_t 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
53
54
55 /* Frees all the memory loaded by the cli */
56 static void cli_free(void)
57 {
58         int i;
59
60         for (i = 0; i <= CLI_HISTORY_MAX - 1; i++) {
61                 if (CommandLines[i])
62                         d_free(CommandLines[i]);
63         }
64
65         d_free(Prompt);
66 }
67
68
69 /* Initializes the cli */
70 void cli_init(void)
71 {
72         TotalCommands = 0;
73         CLI_insert_mode = 1;
74         CursorPos = 0;
75         CommandScrollBack = 0;
76         Prompt = d_strdup(CLI_DEFAULT_PROMPT);
77
78         memset(CommandLines, 0, sizeof(CommandLines));
79         memset(Command, 0, sizeof(Command));
80         memset(LCommand, 0, sizeof(LCommand));
81         memset(RCommand, 0, sizeof(RCommand));
82         memset(VCommand, 0, sizeof(VCommand));
83
84         atexit(cli_free);
85 }
86
87
88 /* Increments the command lines */
89 static void cli_newline(void)
90 {
91         int loop;
92
93         if (CommandLines[CLI_HISTORY_MAX - 1])
94                 d_free(CommandLines[CLI_HISTORY_MAX - 1]);
95
96         for (loop = CLI_HISTORY_MAX - 1; loop > 0; loop--)
97                 CommandLines[loop] = CommandLines[loop - 1];
98
99         CommandLines[0] = NULL;
100
101         if (TotalCommands < CLI_HISTORY_MAX - 1)
102                 TotalCommands++;
103 }
104
105
106 /* Draws the command line the user is typing in to the screen */
107 /* completely rewritten by C.Wacha */
108 void cli_draw(int y)
109 {
110         int x, w, h, aw;
111         float real_aw;
112         size_t commandbuffer;
113         fix cur_time = timer_get_fixed_seconds();
114         static fix LastBlinkTime = 0;   // Last time the cursor blinked
115         static size_t LastCursorPos = 0; // Last cursor position
116         static int Blink = 0;           // Is the cursor currently blinking
117
118         // Concatenate the left and right side to command
119         strcpy(Command, LCommand);
120         strncat(Command, RCommand, sizeof(Command) - strlen(Command) - 1);
121
122         gr_get_string_size(Command, &w, &h, &aw);
123         if (w > 0 && *Command)
124                 real_aw = (float)w/(float)strlen(Command);
125         else
126                 real_aw = (float)aw;
127         commandbuffer = (GWIDTH - 2*CLI_CHAR_BORDER)/real_aw - strlen(Prompt) - 1; // -1 to make cursor visible
128
129         //calculate display offset from current cursor position
130         if (CursorPos > commandbuffer && Offset < CursorPos - commandbuffer)
131                 Offset = CursorPos - commandbuffer;
132         if(Offset > CursorPos)
133                 Offset = CursorPos;
134
135         // first add prompt to visible part
136         strcpy(VCommand, Prompt);
137
138         // then add the visible part of the command
139         strncat(VCommand, &Command[Offset], sizeof(VCommand) - strlen(VCommand) - 1);
140
141         gr_set_fontcolor(CON_color, -1);
142
143         // now display the result
144         gr_string(CLI_CHAR_BORDER, y-h, VCommand);
145
146         // at last add the cursor
147         // check if the blink period is over
148         if (cur_time > LastBlinkTime) {
149                 LastBlinkTime = cur_time + CLI_BLINK_RATE;
150                 if(Blink)
151                         Blink = 0;
152                 else
153                         Blink = 1;
154         }
155
156         // check if cursor has moved - if yes display cursor anyway
157         if (CursorPos != LastCursorPos) {
158                 LastCursorPos = CursorPos;
159                 LastBlinkTime = cur_time + CLI_BLINK_RATE;
160                 Blink = 1;
161         }
162
163         if (Blink) {
164                 int prompt_width, cmd_width, h, w;
165
166                 gr_get_string_size(Prompt, &prompt_width, &h, &w);
167                 gr_get_string_size(LCommand + Offset, &cmd_width, &h, &w);
168                 x = CLI_CHAR_BORDER + prompt_width + cmd_width;
169                 if (CLI_insert_mode)
170                         gr_string(x, y-h, CLI_INS_CURSOR);
171                 else
172                         gr_string(x, y-h, CLI_OVR_CURSOR);
173         }
174 }
175
176
177 /* Executes the command entered */
178 void cli_execute(void)
179 {
180         if(strlen(Command) > 0) {
181                 cli_newline();
182
183                 // copy the input into the past commands strings
184                 CommandLines[0] = d_strdup(Command);
185
186                 // display the command including the prompt
187                 con_printf(CON_NORMAL, "%s%s\n", Prompt, Command);
188
189                 cmd_append(Command);
190
191                 cli_clear();
192                 CommandScrollBack = -1;
193         }
194 }
195
196
197 void cli_autocomplete(void)
198 {
199         size_t i, j;
200         const char *command;
201
202         command = cmd_complete(LCommand);
203
204         if (!command)
205                 return; // no tab completion took place so return silently
206
207         j = strlen(command);
208         if (j > CLI_CHARS_PER_LINE - 2)
209                 j = CLI_CHARS_PER_LINE - 1;
210
211         memset(LCommand, 0, sizeof(LCommand));
212         CursorPos = 0;
213
214         for (i = 0; i < j; i++) {
215                 CursorPos++;
216                 LCommand[i] = command[i];
217         }
218         // add a trailing space
219         CursorPos++;
220         LCommand[j] = ' ';
221         LCommand[j+1] = '\0';
222 }
223
224
225 void cli_cursor_left(void)
226 {
227         char temp[CLI_CHARS_PER_LINE];
228
229         if (CursorPos > 0) {
230                 CursorPos--;
231                 strcpy(temp, RCommand);
232                 strcpy(RCommand, &LCommand[strlen(LCommand)-1]);
233                 strcat(RCommand, temp);
234                 LCommand[strlen(LCommand)-1] = '\0';
235         }
236 }
237
238
239 void cli_cursor_right(void)
240 {
241         char temp[CLI_CHARS_PER_LINE];
242
243         if(CursorPos < strlen(Command)) {
244                 CursorPos++;
245                 strncat(LCommand, RCommand, 1);
246                 strcpy(temp, RCommand);
247                 strcpy(RCommand, &temp[1]);
248         }
249 }
250
251
252 void cli_cursor_home(void)
253 {
254         char temp[CLI_CHARS_PER_LINE];
255
256         CursorPos = 0;
257         strcpy(temp, RCommand);
258         strcpy(RCommand, LCommand);
259         strncat(RCommand, temp, strlen(temp));
260         memset(LCommand, 0, sizeof(LCommand));
261 }
262
263
264 void cli_cursor_end(void)
265 {
266         CursorPos = strlen(Command);
267         strncat(LCommand, RCommand, strlen(RCommand));
268         memset(RCommand, 0, sizeof(RCommand));
269 }
270
271
272 void cli_cursor_del(void)
273 {
274         char temp[CLI_CHARS_PER_LINE];
275
276         if (strlen(RCommand) > 0) {
277                 strcpy(temp, RCommand);
278                 strcpy(RCommand, &temp[1]);
279         }
280 }
281
282
283 void cli_cursor_backspace(void)
284 {
285         if (CursorPos > 0) {
286                 CursorPos--;
287                 if (Offset > 0)
288                         Offset--;
289                 LCommand[strlen(LCommand)-1] = '\0';
290         }
291 }
292
293
294 void cli_add_character(char character)
295 {
296         if (strlen(Command) < CLI_CHARS_PER_LINE - 1)
297         {
298                 CursorPos++;
299                 LCommand[strlen(LCommand)] = character;
300                 LCommand[strlen(LCommand)] = '\0';
301         }
302         if (!CLI_insert_mode)
303                 cli_cursor_del();
304 }
305
306
307 void cli_clear(void)
308 {
309         CursorPos = 0;
310         memset(Command, 0, sizeof(Command));
311         memset(LCommand, 0, sizeof(LCommand));
312         memset(RCommand, 0, sizeof(RCommand));
313         memset(VCommand, 0, sizeof(VCommand));
314 }
315
316
317 void cli_history_prev(void)
318 {
319         if(CommandScrollBack < TotalCommands - 1) {
320                 /* move back a line in the command strings and copy the command to the current input string */
321                 CommandScrollBack++;
322                 memset(RCommand, 0, sizeof(RCommand));
323                 Offset = 0;
324                 strcpy(LCommand, CommandLines[CommandScrollBack]);
325                 CursorPos = strlen(CommandLines[CommandScrollBack]);
326         }
327 }
328
329
330 void cli_history_next(void)
331 {
332         if(CommandScrollBack > -1) {
333                 /* move forward a line in the command strings and copy the command to the current input string */
334                 CommandScrollBack--;
335                 memset(RCommand, 0, sizeof(RCommand));
336                 memset(LCommand, 0, sizeof(LCommand));
337                 Offset = 0;
338                 if(CommandScrollBack > -1)
339                         strcpy(LCommand, CommandLines[CommandScrollBack]);
340                 CursorPos = strlen(LCommand);
341         }
342 }