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