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