1 #ifndef _CONSOLE_H
2 #define _CONSOLE_H
3 /*
4  * z64555's debug console, created for the FreeSpace Source Code project
5  *
6  * Portions of this source code are based on works by Volition, Inc. circa 1999. You may not sell or otherwise
7  * commercially exploit the source or things you created based on the source.
8  */
9 
10 /**
11  * @file console.h
12  * @brief An overhauled/updated debug console to allow monitoring, testing, and general debugging of new features.
13  *
14  * @details
15  * Of key interest is Volition's DCF macro, which adds the function argument to the available command list in the
16  * debug console. These functions may be defined in the .cpp file that they are related to, but it is recommended
17  * that they be in their own .cpp if they have multiple sub-arguments (ex: Git has its sub-arguments delimited by
18  * a pair of -'s, or --)
19  */
20 
21 #include "debugconsole/consoleparse.h"
22 #include "globalincs/pstypes.h"
23 #include "globalincs/vmallocator.h"
24 
25 #define DC_MAX_COMMANDS 300
26 
27 class debug_command;
28 
29 /**
30  * @def DCF
31  *
32  * @brief The potent DCF macro, used to define new debug commands for the console.
33  *
34  * @param function_name[in] The name of the function, as shown in the debug console
35  * @param help_txt[in] The short-help text, as shown as listed from the 'help' command
36  *
37  * @details
38  * Each DCF is responsible for getting data from the debug console's command line. The parsing functions for the
39  * command line have been set up to have similar syntax and usage as the parselo commands. (see consoleparse.h)
40  * Most, if not all, argument and subcommand strings should be parsed by the dc_optional_string functions.
41  * Usage example:
42  * DCF(toggle_it, "description")
43  * {
44  *     if (dc_optional_string_either("help", "--help")) {
45  *         dc_printf("Usage: sample. Toggles This_var on/off\n");
46  *
47  *     } else if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
48  *         dc_printf("This_var is %s\n", (This_var ? "ON" : "OFF"));
49  *
50  *     } else {
51  *         This_var = !This_var;
52  *
53  *     }
54  * }
55  *
56  *  In the console, the command would be listed as 'toggle_it', and dcf_help would display it as:
57  *      toggle_it  - Usage: sample. Toggles This_var on/off.
58  *  Note: The only allowed function type is a void fn( void )
59  */
60 #define DCF(function_name, help_text)	\
61 		void dcf_##function_name();	\
62 		debug_command dcmd_##function_name(#function_name, help_text, dcf_##function_name);	\
63 		void dcf_##function_name()
64 
65 /**
66  *  @def Shortcut for debug commands that toggle a bool, such as Show_lightning
67  *
68  *  @param [in] function_name Name of the function, as shown in the debug console
69  *  @param [in] bool_variable Name of the variable to allow toggling.
70  */
71 #define DCF_BOOL(function_name, bool_variable)	\
72 	void dcf_##function_name();		\
73 	debug_command dcmd_##function_name(#function_name, "Sets or toggles the boolean: "#bool_variable, dcf_##function_name );	\
74 	void dcf_##function_name() {	\
75 		bool bool_tmp = bool_variable != 0;	\
76 		if (dc_optional_string_either("help", "--help")) {	\
77 				dc_printf( "Usage: %s [bool]\nSets %s to true or false.  If nothing passed, then toggles it.\n", #function_name, #bool_variable );	\
78 				return;		\
79 		}	\
80 		if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {	\
81 			dc_printf("%s = %s\n", #bool_variable, (bool_variable ? "TRUE" : "FALSE"));		\
82 			return;		\
83 		}	\
84 		if (!dc_maybe_stuff_boolean(&bool_tmp)) {	\
85 			if (bool_variable != 0) \
86 				bool_variable = 0; \
87 			else \
88 				bool_variable = 1;	\
89 		} else { \
90 			if (bool_tmp) \
91 				bool_variable = 1; \
92 			else \
93 				bool_variable = 0;	\
94 		}	\
95 		dc_printf("%s set to %s\n", #bool_variable, (bool_variable != 0 ? "TRUE" : "FALSE"));	\
96 	}
97 
98 
99 /**
100  *  @def Same as DCF_BOOL, but with custom help strings
101  *
102  *  @param [in] function_name Name of the function, as shown in the debug console
103  *  @param [in] bool_variable Name of the variable to allow toggling.
104  */
105 #define DCF_BOOL2(function_name, bool_variable, short_help, long_help)	\
106 	void dcf_##function_name();		\
107 	debug_command dcmd_##function_name(#function_name, short_help, dcf_##function_name );	\
108 	void dcf_##function_name() {	\
109 		if (dc_optional_string_either("help", "--help")) {	\
110 			dc_printf( #long_help );	\
111 				return;		\
112 		}	\
113 		if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {	\
114 			dc_printf("%s = %s\n", #function_name, (bool_variable ? "TRUE" : "FALSE"));		\
115 			return;		\
116 		}	\
117 		if (!dc_maybe_stuff_boolean(&bool_variable)) {	\
118 			bool_variable = !bool_variable;	\
119 		}	\
120 	}
121 
122  /**
123   * @def Shortcut for single-variable setters/monitors
124   *
125   * @param [in] function_name
126   * @param [in] float_variable
127   * @param [in] short_help
128   */
129  #define DCF_FLOAT(function_name, float_variable, short_help)	\
130 	void dcf_##function_name();		\
131 	debug_command dcmd_##function_name(#function_name, short_help, dcf_##function_name );	\
132 	void dcf_##function_name() {	\
133 		float value;	\
134 		if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {	\
135 			dc_printf("%s = %f\n", #float_variable, float_variable);		\
136 			return;		\
137 		}	\
138 		dc_stuff_float(&value);	\
139 		float_variable = value;	\
140 		dc_printf("%s set to %f\n", #float_variable, float_variable);	\
141 	}
142 
143  /**
144   * @def Shortcut for single-variable setters/monitors with lower/upper bounds clamping
145   *
146   * @param [in] function_name
147   * @param [in] float_variable
148   * @param [in] lower_bounds
149   * @param [in] upper_bounds
150   * @param [in] short_help
151   */
152  #define DCF_FLOAT2(function_name, float_variable, lower_bounds, upper_bounds, short_help)	\
153 	void dcf_##function_name();		\
154 	debug_command dcmd_##function_name(#function_name, short_help, dcf_##function_name );	\
155 	void dcf_##function_name() {	\
156 		float value;	\
157 		if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {	\
158 			dc_printf("%s = %f\n", #float_variable, float_variable);		\
159 			return;		\
160 		}	\
161 		dc_stuff_float(&value);	\
162 		CLAMP(float_variable, lower_bounds, upper_bounds);	\
163 		float_variable = value;	\
164 		dc_printf("%s set to %f\n", #float_variable, float_variable);	\
165 	}
166 
167  /**
168   * @def Shortcut for single-variable setters/monitors
169   *
170   * @param [in] function_name
171   * @param [in] int_variable
172   * @param [in] short_help
173   */
174  #define DCF_INT(function_name, int_variable, short_help)	\
175 	void dcf_##function_name();		\
176 	debug_command dcmd_##function_name(#function_name, short_help, dcf_##function_name );	\
177 	void dcf_##function_name() {	\
178 		int value;	\
179 		if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {	\
180 			dc_printf("%s = %i\n", #int_variable, int_variable);		\
181 			return;		\
182 		}	\
183 		dc_stuff_int(&value);	\
184 		int_variable = value;	\
185 		dc_printf("%s set to %i\n", #int_variable, int_variable);	\
186 	}
187 
188  /**
189   * @def Shortcut for single-variable setters/monitors with lower/upper bounds clamping
190   *
191   * @param [in] function_name
192   * @param [in] int_variable
193   * @param [in] lower_bounds
194   * @param [in] upper_bounds
195   * @param [in] short_help
196   */
197  #define DCF_INT2(function_name, int_variable, lower_bounds, upper_bounds, short_help)	\
198 	void dcf_##function_name();		\
199 	debug_command dcmd_##function_name(#function_name, short_help, dcf_##function_name );	\
200 	void dcf_##function_name() {	\
201 		int value;	\
202 		if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {	\
203 			dc_printf("%s = %i\n", #int_variable, int_variable);		\
204 			return;		\
205 		}	\
206 		dc_stuff_int(&value);	\
207 		CLAMP(int_variable, lower_bounds, upper_bounds);	\
208 		int_variable = value;	\
209 		dc_printf("%s set to %i\n", #int_variable, int_variable);	\
210 	}
211 
212 /**
213  *  @class debug_command
214  *  @brief Class to aggregate a debug command with its name (as shown in the console) and short help.
215  *
216  *  @details
217  *  Note: Long help, as evoked by '<command> help', should be handled by the function itself. It is recommended
218  *  that arguments that have sub-arguments be in their own function, so as to aide in organization and to keep the
219  *  size of the command function down.
220  */
221 class debug_command {
222 public:
223 	const char *name;		//!< The name of the command, as shown in the debug console
224 	const char *help;		//!< The short help string, as shown by 'help <command>'
225 	void (*func)();	//!< Pointer to the function that to run when this command is evoked
226 
227 	debug_command();
228 
229 	/**
230 	* @brief Adds a debug command to the debug_commands map, if it isn't in there already.
231 	*
232 	* @details The DCF macro more or less guarantees that a command won't be duplicated on compile time. But no harm in
233 	*   some extra caution.
234 	*/
235 	debug_command(const char *name, const char *help, void (*func)());
236 };
237 
238 /**
239  * @class is_dcmd
240  * @brief Predicate class used to search for a dcmd by name
241  */
242 class is_dcmd {
243 public:
244 	const char *name;
245 
is_dcmd(const char * _name)246 	is_dcmd(const char *_name) : name(_name) {}
247 
operator()248 	bool operator() (debug_command* dcmd)
249 	{
250 		return (strcmp(name, dcmd->name) == 0);
251 	}
252 };
253 
254 extern bool Dc_debug_on;
255 extern int dc_commands_size;
256 extern uint lastline;
257 extern SCP_string dc_command_str;	// The rest of the command line, from the end of the last processed arg on.
258 
259 /**
260  * @brief   Pauses the output of a command and allows user to scroll through the output history.
261  *
262  * @details Returns true if user has pressed Esc, returns false otherwise. Use this in your function to (safely?) break
263  *     out of the loop it's presumably in.
264  */
265 bool dc_pause_output(void);
266 
267 /**
268  * @brief Prints the given char string to the debug console
269  * @details See the doc for std::printf() for formating and more details
270  */
271 void dc_printf(SCP_FORMAT_STRING const char *format, ...) SCP_FORMAT_STRING_ARGS(1, 2);
272 
273 /**
274  * @brief Opens and processes the debug console. (Blocking call)
275  * @details TODO: Make this a non-blocking call so that the game can still run while the debug console is open.
276  */
277 void debug_console(void (*func)(void) = NULL);
278 
279 #endif // _CONSOLE_H
280