]> icculus.org git repositories - btb/d2x.git/blob - main/cli.c
miscellaneous fixes
[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 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
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()
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, 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);
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         int commandbuffer;
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
117
118         // Concatenate the left and right side to command
119         strcpy(Command, LCommand);
120         strncat(Command, RCommand, CLI_CHARS_PER_LINE - strlen(Command));
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 - (int)strlen(Prompt) - 1; // -1 to make cursor visible
128
129         //calculate display offset from current cursor position
130         if (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], CLI_CHARS_PER_LINE - strlen(VCommand));
140
141         // now display the result
142         gr_string(CLI_CHAR_BORDER, y-h, VCommand);
143
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;
148                 if(Blink)
149                         Blink = 0;
150                 else
151                         Blink = 1;
152         }
153
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;
158                 Blink = 1;
159         }
160
161         if (Blink) {
162                 int prompt_width, cmd_width, h, w;
163
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;
167                 if (CLI_insert_mode)
168                         gr_string(x, y-h, CLI_INS_CURSOR);
169                 else
170                         gr_string(x, y-h, CLI_OVR_CURSOR);
171         }
172 }
173
174
175 /* Executes the command entered */
176 void cli_execute(void)
177 {
178         if(strlen(Command) > 0) {
179                 cli_newline();
180
181                 // copy the input into the past commands strings
182                 CommandLines[0] = d_strdup(Command);
183
184                 // display the command including the prompt
185                 con_printf(CON_NORMAL, "%s%s\n", Prompt, Command);
186
187                 cmd_append(Command);
188
189                 cli_clear();
190                 CommandScrollBack = -1;
191         }
192 }
193
194
195 void cli_autocomplete(void)
196 {
197         int i, j;
198         const char *command;
199
200         command = cmd_complete(LCommand);
201
202         if (!command)
203                 return; // no tab completion took place so return silently
204
205         j = (int)strlen(command);
206         if (j > CLI_CHARS_PER_LINE - 2)
207                 j = CLI_CHARS_PER_LINE - 1;
208
209         memset(LCommand, 0, CLI_CHARS_PER_LINE);
210         CursorPos = 0;
211
212         for (i = 0; i < j; i++) {
213                 CursorPos++;
214                 LCommand[i] = command[i];
215         }
216         // add a trailing space
217         CursorPos++;
218         LCommand[j] = ' ';
219         LCommand[j+1] = '\0';
220 }
221
222
223 void cli_cursor_left(void)
224 {
225         char temp[CLI_CHARS_PER_LINE];
226
227         if (CursorPos > 0) {
228                 CursorPos--;
229                 strcpy(temp, RCommand);
230                 strcpy(RCommand, &LCommand[strlen(LCommand)-1]);
231                 strcat(RCommand, temp);
232                 LCommand[strlen(LCommand)-1] = '\0';
233         }
234 }
235
236
237 void cli_cursor_right(void)
238 {
239         char temp[CLI_CHARS_PER_LINE];
240
241         if(CursorPos < strlen(Command)) {
242                 CursorPos++;
243                 strncat(LCommand, RCommand, 1);
244                 strcpy(temp, RCommand);
245                 strcpy(RCommand, &temp[1]);
246         }
247 }
248
249
250 void cli_cursor_home(void)
251 {
252         char temp[CLI_CHARS_PER_LINE];
253
254         CursorPos = 0;
255         strcpy(temp, RCommand);
256         strcpy(RCommand, LCommand);
257         strncat(RCommand, temp, strlen(temp));
258         memset(LCommand, 0, CLI_CHARS_PER_LINE);
259 }
260
261
262 void cli_cursor_end(void)
263 {
264         CursorPos = (int)strlen(Command);
265         strncat(LCommand, RCommand, strlen(RCommand));
266         memset(RCommand, 0, CLI_CHARS_PER_LINE);
267 }
268
269
270 void cli_cursor_del(void)
271 {
272         char temp[CLI_CHARS_PER_LINE];
273
274         if (strlen(RCommand) > 0) {
275                 strcpy(temp, RCommand);
276                 strcpy(RCommand, &temp[1]);
277         }
278 }
279
280
281 void cli_cursor_backspace(void)
282 {
283         if (CursorPos > 0) {
284                 CursorPos--;
285                 Offset--;
286                 if (Offset < 0)
287                         Offset = 0;
288                 LCommand[strlen(LCommand)-1] = '\0';
289         }
290 }
291
292
293 void cli_add_character(char character)
294 {
295         if (strlen(Command) < CLI_CHARS_PER_LINE - 1)
296         {
297                 CursorPos++;
298                 LCommand[strlen(LCommand)] = character;
299                 LCommand[strlen(LCommand)] = '\0';
300         }
301         if (!CLI_insert_mode)
302                 cli_cursor_del();
303 }
304
305
306 void cli_clear(void)
307 {
308         CursorPos = 0;
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);
313 }
314
315
316 void cli_history_prev(void)
317 {
318         if(CommandScrollBack < TotalCommands - 1) {
319                 /* move back a line in the command strings and copy the command to the current input string */
320                 CommandScrollBack++;
321                 memset(RCommand, 0, CLI_CHARS_PER_LINE);
322                 Offset = 0;
323                 strcpy(LCommand, CommandLines[CommandScrollBack]);
324                 CursorPos = (int)strlen(CommandLines[CommandScrollBack]);
325         }
326 }
327
328
329 void cli_history_next(void)
330 {
331         if(CommandScrollBack > -1) {
332                 /* move forward a line in the command strings and copy the command to the current input string */
333                 CommandScrollBack--;
334                 memset(RCommand, 0, CLI_CHARS_PER_LINE);
335                 memset(LCommand, 0, CLI_CHARS_PER_LINE);
336                 Offset = 0;
337                 if(CommandScrollBack > -1)
338                         strcpy(LCommand, CommandLines[CommandScrollBack]);
339                 CursorPos = (int)strlen(LCommand);
340         }
341 }