]> icculus.org git repositories - btb/d2x.git/blob - main/cli.c
clean up names
[btb/d2x.git] / main / cli.c
1 /*
2  *  Code for controlling the console
3  *  Based on an early version of SDL_Console
4  *
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 get_msecs() approx_fsec_to_msec(timer_get_approx_seconds())
28
29 #define CLI_NUM_LINES           128
30 // Cut the buffer line if it becomes longer than this
31 #define CLI_CHARS_PER_LINE      128
32 // Cursor blink frequency in ms
33 #define CLI_BLINK_RATE          500
34 // Border in pixels from the most left to the first letter
35 #define CLI_CHAR_BORDER         4
36 // Default prompt used at the commandline
37 #define CLI_DEFAULT_PROMPT      "]"
38 // Cursor shown if we are in insert mode
39 #define CLI_INS_CURSOR          "_"
40 // Cursor shown if we are in overwrite mode
41 #define CLI_OVR_CURSOR          "|"
42
43 int CLI_insert_mode;            // Insert or Overwrite characters?
44
45 static char **CommandLines;     // List of all the past commands
46 static int TotalCommands;       // Number of commands in the Back Commands
47 static int LineBuffer;          // The number of visible lines in the console (autocalculated)
48 static char *Prompt;            // Prompt displayed in command line
49 static char  Command[CLI_CHARS_PER_LINE];   // current command in command line = lcommand + rcommand
50 static char LCommand[CLI_CHARS_PER_LINE];   // right hand side of cursor
51 static char RCommand[CLI_CHARS_PER_LINE];   // left hand side of cursor
52 static char VCommand[CLI_CHARS_PER_LINE];   // current visible command line
53 static int CursorPos;           // Current cursor position in CurrentCommand
54 static int Offset;              // CommandOffset (first visible char of command) - if command is too long to fit into console
55 static int CommandScrollBack;   // How much the users scrolled back in the command lines
56
57
58 /* Frees all the memory loaded by the cli */
59 static void cli_free(void);
60 /* shift command history (the one you can switch with the up/down keys) */
61 static void cli_newline(void);
62
63
64 /* Initializes the cli */
65 void cli_init()
66 {
67         int loop;
68
69         CommandLines = NULL;
70         TotalCommands = 0;
71         CLI_insert_mode = 1;
72         CursorPos = 0;
73         CommandScrollBack = 0;
74         Prompt = d_strdup(CLI_DEFAULT_PROMPT);
75         LineBuffer = CLI_NUM_LINES;
76
77         CommandLines = (char **)d_malloc(sizeof(char *) * LineBuffer);
78         for (loop = 0; loop <= LineBuffer - 1; loop++) {
79                 CommandLines[loop] = (char *)d_calloc(CLI_CHARS_PER_LINE, sizeof(char));
80         }
81         memset(Command, 0, CLI_CHARS_PER_LINE);
82         memset(LCommand, 0, CLI_CHARS_PER_LINE);
83         memset(RCommand, 0, CLI_CHARS_PER_LINE);
84         memset(VCommand, 0, CLI_CHARS_PER_LINE);
85
86         atexit(cli_free);
87 }
88
89
90 /* Frees all the memory loaded by the console */
91 static void cli_free(void)
92 {
93         int i;
94
95         for (i = 0; i <= LineBuffer - 1; i++) {
96                 d_free(CommandLines[i]);
97         }
98         d_free(CommandLines);
99
100         CommandLines = NULL;
101
102         d_free(Prompt);
103 }
104
105
106 /* Increments the command lines */
107 static void cli_newline(void)
108 {
109         int loop;
110         char *temp;
111
112         temp  = CommandLines[LineBuffer - 1];
113
114         for (loop = LineBuffer - 1; loop > 0; loop--)
115                 CommandLines[loop] = CommandLines[loop - 1];
116
117         CommandLines[0] = temp;
118
119         memset(CommandLines[0], 0, CLI_CHARS_PER_LINE);
120         if (TotalCommands < LineBuffer - 1)
121                 TotalCommands++;
122 }
123
124
125 /* Draws the command line the user is typing in to the screen */
126 /* completely rewritten by C.Wacha */
127 void cli_draw(int y)
128 {
129         int x, w, h, aw;
130         float real_aw;
131         int commandbuffer;
132         static unsigned int LastBlinkTime = 0;  // Last time the consoles cursor blinked
133         static int LastCursorPos = 0;           // Last Cursor Position
134         static int Blink = 0;                   // Is the cursor currently blinking
135
136         // Concatenate the left and right side to command
137         strcpy(Command, LCommand);
138         strncat(Command, RCommand, strlen(RCommand));
139
140         gr_get_string_size(Command, &w, &h, &aw);
141         if (w > 0 && strlen(Command))
142                 real_aw = (float)w/(float)strlen(Command);
143         else
144                 real_aw = (float)aw;
145         commandbuffer = (GWIDTH - 2*CLI_CHAR_BORDER)/real_aw - (int)strlen(Prompt) - 1; // -1 to make cursor visible
146
147         //calculate display offset from current cursor position
148         if (Offset < CursorPos - commandbuffer)
149                 Offset = CursorPos - commandbuffer;
150         if(Offset > CursorPos)
151                 Offset = CursorPos;
152
153         // first add prompt to visible part
154         strcpy(VCommand, Prompt);
155
156         // then add the visible part of the command
157         strncat(VCommand, &Command[Offset], strlen(&Command[Offset]));
158
159         // now display the result
160         gr_string(CLI_CHAR_BORDER, y-h, VCommand);
161
162         // at last add the cursor
163         // check if the blink period is over
164         if (get_msecs() > LastBlinkTime) {
165                 LastBlinkTime = get_msecs() + CLI_BLINK_RATE;
166                 if(Blink)
167                         Blink = 0;
168                 else
169                         Blink = 1;
170         }
171
172         // check if cursor has moved - if yes display cursor anyway
173         if (CursorPos != LastCursorPos) {
174                 LastCursorPos = CursorPos;
175                 LastBlinkTime = get_msecs() + CLI_BLINK_RATE;
176                 Blink = 1;
177         }
178
179         if (Blink) {
180                 int prompt_width, cmd_width, h, w;
181
182                 gr_get_string_size(Prompt, &prompt_width, &h, &w);
183                 gr_get_string_size(LCommand + Offset, &cmd_width, &h, &w);
184                 x = CLI_CHAR_BORDER + prompt_width + cmd_width;
185                 if (CLI_insert_mode)
186                         gr_string(x, y-h, CLI_INS_CURSOR);
187                 else
188                         gr_string(x, y-h, CLI_OVR_CURSOR);
189         }
190 }
191
192
193 /* Executes the command entered */
194 void cli_execute(void)
195 {
196         if(strlen(Command) > 0) {
197                 cli_newline();
198
199                 // copy the input into the past commands strings
200                 strcpy(CommandLines[0], Command);
201
202                 // display the command including the prompt
203                 con_out("%s%s\n", Prompt, Command);
204
205                 cmd_append(Command);
206
207                 cli_clear();
208                 CommandScrollBack = -1;
209         }
210 }
211
212
213 void cli_autocomplete(void)
214 {
215         int i, j;
216         char *command;
217
218         command = cmd_complete(LCommand);
219
220         if (!command)
221                 return; // no tab completion took place so return silently
222
223         j = (int)strlen(command);
224         if (j > CLI_CHARS_PER_LINE - 2)
225                 j = CLI_CHARS_PER_LINE-1;
226
227         memset(LCommand, 0, CLI_CHARS_PER_LINE);
228         CursorPos = 0;
229
230         for (i = 0; i < j; i++) {
231                 CursorPos++;
232                 LCommand[i] = command[i];
233         }
234         // add a trailing space
235         CursorPos++;
236         LCommand[j] = ' ';
237         LCommand[j+1] = '\0';
238 }
239
240
241 void cli_cursor_left(void)
242 {
243         char temp[CLI_CHARS_PER_LINE];
244
245         if (CursorPos > 0) {
246                 CursorPos--;
247                 strcpy(temp, RCommand);
248                 strcpy(RCommand, &LCommand[strlen(LCommand)-1]);
249                 strcat(RCommand, temp);
250                 LCommand[strlen(LCommand)-1] = '\0';
251         }
252 }
253
254
255 void cli_cursor_right(void)
256 {
257         char temp[CLI_CHARS_PER_LINE];
258
259         if(CursorPos < strlen(Command)) {
260                 CursorPos++;
261                 strncat(LCommand, RCommand, 1);
262                 strcpy(temp, RCommand);
263                 strcpy(RCommand, &temp[1]);
264         }
265 }
266
267
268 void cli_cursor_home(void)
269 {
270         char temp[CLI_CHARS_PER_LINE];
271
272         CursorPos = 0;
273         strcpy(temp, RCommand);
274         strcpy(RCommand, LCommand);
275         strncat(RCommand, temp, strlen(temp));
276         memset(LCommand, 0, CLI_CHARS_PER_LINE);
277 }
278
279
280 void cli_cursor_end(void)
281 {
282         CursorPos = (int)strlen(Command);
283         strncat(LCommand, RCommand, strlen(RCommand));
284         memset(RCommand, 0, CLI_CHARS_PER_LINE);
285 }
286
287
288 void cli_cursor_del(void)
289 {
290         char temp[CLI_CHARS_PER_LINE];
291
292         if (strlen(RCommand) > 0) {
293                 strcpy(temp, RCommand);
294                 strcpy(RCommand, &temp[1]);
295         }
296 }
297
298 void cli_cursor_backspace(void)
299 {
300         if (CursorPos > 0) {
301                 CursorPos--;
302                 Offset--;
303                 if (Offset < 0)
304                         Offset = 0;
305                 LCommand[strlen(LCommand)-1] = '\0';
306         }
307 }
308
309
310 void cli_add_character(char character)
311 {
312         if (strlen(Command) < CLI_CHARS_PER_LINE - 1)
313         {
314                 CursorPos++;
315                 LCommand[strlen(LCommand)] = character;
316                 LCommand[strlen(LCommand)] = '\0';
317         }
318 }
319
320
321 void cli_clear(void)
322 {
323         CursorPos = 0;
324         memset( Command, 0, CLI_CHARS_PER_LINE);
325         memset(LCommand, 0, CLI_CHARS_PER_LINE);
326         memset(RCommand, 0, CLI_CHARS_PER_LINE);
327         memset(VCommand, 0, CLI_CHARS_PER_LINE);
328 }
329
330
331 void cli_history_prev(void)
332 {
333         if(CommandScrollBack < TotalCommands - 1) {
334                 /* move back a line in the command strings and copy the command to the current input string */
335                 CommandScrollBack++;
336                 memset(RCommand, 0, CLI_CHARS_PER_LINE);
337                 Offset = 0;
338                 strcpy(LCommand, CommandLines[CommandScrollBack]);
339                 CursorPos = (int)strlen(CommandLines[CommandScrollBack]);
340         }
341 }
342
343
344 void cli_history_next(void)
345 {
346         if(CommandScrollBack > -1) {
347                 /* move forward a line in the command strings and copy the command to the current input string */
348                 CommandScrollBack--;
349                 memset(RCommand, 0, CLI_CHARS_PER_LINE);
350                 memset(LCommand, 0, CLI_CHARS_PER_LINE);
351                 Offset = 0;
352                 if(CommandScrollBack > -1)
353                         strcpy(LCommand, CommandLines[CommandScrollBack]);
354                 CursorPos = (int)strlen(LCommand);
355         }
356 }