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