EDIT: Code and example updated!
Because trying to work with a log file became annoying, I created a class that can be used to output directly to the debug console.
It is a standalone class. And I've tested in Mac, iOS, Android and Windows.
Proper usage is to call the init_console method in app::Begin and the close_console in app::End. The send_to_console method can be called anywhere.
Header file (btw, I am Triassic Associates, Inc.):
// ******************************************************************************
//
// TADebugConsole - Debug output to console class
//
// Version 1.10
//
// Author: Cynthia R. Douglas, Triassic Associates, Inc.
//
// License: GPL, see http://www.gnu.org/licenses/gpl.html
//
// This class was originally created by Triassic Associates, Inc.
// for use in its own applications.
//
// It is being made available for public use "AS IS".
//
// Please keep this disclaimer.
//
// ******************************************************************************
#ifndef _TA_DEBUG_CONSOLE_
#define _TA_DEBUG_CONSOLE_
// get some standard objects we need
#include <string>
#include <stdarg.h>
// other includes as needed by platform
#ifdef AGKWINDOWS
#include <windows.h>
#endif
#ifdef IDE_ANDROID
#include <android/log.h>
#endif
#if defined(AGKIOS) || defined(IDE_MAC)
#include <asl.h>
#endif
/////////////////////////////////////////
// CLASS NOTES //////////////////////////
/////////////////////////////////////////
/*
To use in Windows:
1. Adjust the display position and size of the console
by changing the parameters in the MoveWindow call
2. If you want to catch statements sent during your app
closing process, you need to make sure not to actually
exit the app. The sample template.h/.cpp show one way.
To use in Android:
1. Connect via USB
2. Run app from Eclipse
3. Monitor 'LogCat' output window
To use in Mac:
1. Run app from Xcode
2. Monitor 'All Output' window
To use in iOS
1. Connect via Apple cable
2. Run app from Xcode
3. Monitor 'All Output' window
*/
/////////////////////////////////////////
// TYPES ////////////////////////////////
/////////////////////////////////////////
typedef enum taDebugConsole_logType
{
TA_DEBUG_CONSOLE_DEBUG = 0,
TA_DEBUG_CONSOLE_INFO = 1,
TA_DEBUG_CONSOLE_WARN = 2,
TA_DEBUG_CONSOLE_ERROR = 3
} taDebugConsole_logType;
/////////////////////////////////////////
// CLASS DEFINITION /////////////////////
/////////////////////////////////////////
class TADebugConsole {
private:
// static attributes for all
static bool did_not_init;
static char app_tag[128];
static char tmp_str[4096];
static char tag_str[256];
static const char* msg_types[4];
// static attributes as needed by platform
#ifdef AGKWINDOWS
static HWND winHandle;
static HANDLE stdHandle;
static DWORD dword;
static char full_str[5120];
#endif
#if defined(AGKIOS) || defined(IDE_MAC)
static aslclient _asl_client;
#endif
public:
// public static methods
/*
Name: init_console
Purpose:
Sets up information and makes connections as necessary.
If not called, all calls to send_to_console are ignored.
Recommendation is to call in the app::Begin method.
Parameters:
apptag - required by all, any text up to 127 characters that helps
you identify messages sent by this class.
asl_fac - required for iOS/Mac and ignored by others.
This is the 'facility' associated with the asl commands.
Can be any text, but recommend using the package name
followed by '.syslog'.
Eg. TADebugConsole::init_console("MyAppDebug","com.yourcompany.yourapp.syslog");
Returns: nothing
*/
static void init_console(const char* apptag,const char* asl_fac=NULL);
/*
Name: close_console
Purpose:
Nothing done if init_console is not called.
Closes connections as needed (required by iOS/Mac, run during app::End method).
Parameters: none
Returns: nothing
*/
static void close_console();
/*
Name: send_to_console
Purpose:
Nothing done if init_console is not called.
Send the passed message with proper type to the debug console.
Parameters:
msgtype - type of message, must be a taDebugConsole_logType value
fmt, ... - inputs like printf for building message.
Eg. TADebugConsole::send_to_console(TA_DEBUG_CONSOLE_INFO,"%s %d","message number",15);
Returns: nothing
*/
static void send_to_console(int msgtype,const char* fmt, ...);
};
#endif
Code file:
// ******************************************************************************
//
// TADebugConsole - Debug output to console class
//
// Version 1.10
//
// Author: Cynthia R. Douglas, Triassic Associates, Inc.
//
// License: GPL, see http://www.gnu.org/licenses/gpl.html
//
// This class was originally created by Triassic Associates, Inc.
// for use in its own applications.
//
// It is being made available for public use "AS IS".
//
// Please keep this disclaimer.
//
// ******************************************************************************
#include "ta_debug_console.h"
/////////////////////////////////////////
// CLASS STATIC ATTRIBUTES //////////////
/////////////////////////////////////////
bool TADebugConsole::did_not_init = true;
char TADebugConsole::app_tag[128];
char TADebugConsole::tmp_str[4096];
char TADebugConsole::tag_str[256];
const char* TADebugConsole::msg_types[4] = {"<Debug>","<Info>","<Warn","<Error>"};
// static attributes as needed by platform
#ifdef AGKWINDOWS
HWND TADebugConsole::winHandle;
HANDLE TADebugConsole::stdHandle;
DWORD TADebugConsole::dword;
char TADebugConsole::full_str[5120];
#endif
#if defined(AGKIOS) || defined(IDE_MAC)
aslclient TADebugConsole::_asl_client;
#endif
/////////////////////////////////////////
// CLASS IMPLEMENTATION /////////////////
/////////////////////////////////////////
void TADebugConsole::init_console(const char* apptag,const char* asl_fac/*=NULL*/)
{
// copy the tag
strncpy(app_tag,apptag,127);
// other inits as needed by platform
#ifdef AGKWINDOWS
// some variables
COORD coord;
SMALL_RECT windowSize;
CONSOLE_SCREEN_BUFFER_INFO coninfo;
// attempt to create a console
if (!AllocConsole())
{
// oops, failed
fprintf( stderr, "Impossible to create the ASL client\n" );
exit( EXIT_FAILURE );
}
// set the title
SetConsoleTitle(apptag);
// get the window handle
winHandle = GetConsoleWindow();
// move it (adjust the position and size here)
MoveWindow(winHandle,2010,10,700,700,true);
// get the standard output handle
stdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
// get maximum allowed size and the font size
coord = GetLargestConsoleWindowSize(stdHandle);
// set the screen buffer to be big enough to let us scroll text
GetConsoleScreenBufferInfo(stdHandle,&coninfo);
// reset console buffer size to max
coninfo.dwSize.X = coord.X;
coninfo.dwSize.Y = coord.Y;
if (!SetConsoleScreenBufferSize(stdHandle,coninfo.dwSize)) TADebugConsole::send_to_console(TA_DEBUG_CONSOLE_ERROR,"SetConsoleScreenBufferSize: %d",GetLastError());
// calculate size we want
windowSize.Top = 0;
windowSize.Left = 0;
windowSize.Bottom = windowSize.Top + coord.Y - 11;
windowSize.Right = windowSize.Left + (coord.X >= 120 ? 120 : coord.X) - 1;
// try to set the size and location
if (!SetConsoleWindowInfo(stdHandle,true,&windowSize)) TADebugConsole::send_to_console(TA_DEBUG_CONSOLE_ERROR,"SetConsoleWindowInfo: %d",GetLastError());
#endif
#if defined(AGKIOS) || defined(IDE_MAC)
// make the connection immediately, as stderr and don't pre-filter messages
_asl_client = asl_open( "TADebugConsole", asl_fac, ASL_OPT_STDERR | ASL_OPT_NO_DELAY | ASL_OPT_NO_REMOTE);
// check the connection
if( _asl_client == NULL )
{
// oops, failed
fprintf( stderr, "Impossible to create the ASL client\n" );
exit( EXIT_FAILURE );
}
// set the filter to make sure that all of our messages are displayed
asl_set_filter(_asl_client,ASL_FILTER_MASK(ASL_LEVEL_DEBUG)|ASL_FILTER_MASK(ASL_LEVEL_INFO)|ASL_FILTER_MASK(ASL_LEVEL_WARNING)|ASL_FILTER_MASK(ASL_LEVEL_ERR));
#endif
// indicate initted
did_not_init = false;
// send message to show done
TADebugConsole::send_to_console(TA_DEBUG_CONSOLE_INFO,"TADebugConsole::init_console(%s,%s)",app_tag,(asl_fac == NULL ? "NULL" : asl_fac));
}
void TADebugConsole::close_console()
{
// check for initted
if (did_not_init) return;
// this is handled according to platform
#ifdef AGKWINDOWS
// free the console
FreeConsole();
#endif
#if defined(AGKIOS) || defined(IDE_MAC)
// close the connection
asl_close(_asl_client);
#endif
}
void TADebugConsole::send_to_console(int msgtype,const char* fmt, ...)
{
// check for initted
if (did_not_init) return;
// handle the input
va_list args;
// create our own string
va_start(args,fmt);
vsnprintf(tmp_str, 4096, fmt, args);
va_end(args);
// make the tag string
sprintf(tag_str,"%s%s",app_tag,msg_types[msgtype]);
// this is now handled according to platform
#ifdef AGKWINDOWS
// clear the string
memset(full_str,0,5120);
// construct the full string
sprintf(full_str,"%s: %s\n",tag_str,tmp_str);
// get the string length
int len = strlen(full_str);
// output to console
WriteConsole(stdHandle,full_str,len,&dword,NULL);
#endif
#ifdef IDE_ANDROID
// call function directly
__android_log_print(ANDROID_LOG_DEBUG+msgtype, tag_str, tmp_str);
#endif
#if defined(AGKIOS) || defined(IDE_MAC)
// decide asl message level
int asl_lev = (msgtype == TA_DEBUG_CONSOLE_DEBUG
? ASL_LEVEL_DEBUG
: (msgtype == TA_DEBUG_CONSOLE_INFO
? ASL_LEVEL_INFO
: ((msgtype == TA_DEBUG_CONSOLE_WARN
? ASL_LEVEL_WARNING : ASL_LEVEL_ERR))));
// send the message
asl_log(_asl_client, NULL, asl_lev, "%s",tmp_str);
#endif
}
Usage example, header file:
#ifndef _H_APP
#define _H_APP
/////////////////////////////////////////
// CONSTANTS ////////////////////////////
/////////////////////////////////////////
#if defined(AGKWINDOWS) || defined(IDE_MAC)
#define DEVICE_WIDTH 960
#define DEVICE_HEIGHT 640
#define DEVICE_POS_X 32
#define DEVICE_POS_Y 32
#define FULLSCREEN false
#define WINDOW_TITLE "Your App Name!"
#endif
// Link to AGK libraries
#include "agk.h"
/////////////////////////////////////////
// CLASS DEFINITION /////////////////////
/////////////////////////////////////////
class app
{
private:
unsigned short app_state; // state app is in
bool did_end; // flag for game has ended
char app_err_msg[4096]; // error message for error state
public:
// main vars
#ifdef IDE_MAC
unsigned int m_DeviceWidth;
unsigned int m_DeviceHeight;
#endif
public:
#if defined(IDE_ANDROID) || defined(AGKIOS) || defined(IDE_MAC)
// constructor
app() {}
// destructor
~app() {}
#else
// constructor
app() {memset ( this, 0, sizeof(app));}
#endif
// main app functions
void Begin( void );
void Loop( void );
void End( void );
private:
void closeThisApp(bool noforce=true);
void checkForExit();
void hadAnAppError(const char* errmsg);
void inBadState();
};
extern app App;
#endif
// Allow us to use the LoadImage function name
#ifdef LoadImage
#undef LoadImage
#endif
Usage example, code file:
// Includes, namespace and prototypes
#include "template.h"
#include "ta_debug_console.h"
using namespace AGK;
app App;
/////////////////////////////////////////
// CONSTANTS ////////////////////////////
/////////////////////////////////////////
// game states
#define _IN_START_INIT_ 1
#define _IN_INITIALISATION_ 2
#define _IN_GAME_PLAY_ 3
#define _IN_BAD_STATE_ 4
// indicate whether using DebugConsole
#define _USE_DEBUG_CONSOLE_ true
/////////////////////////////////////////
// PROTOTYPES ///////////////////////////
/////////////////////////////////////////
#ifdef IDE_ANDROID
extern void exit(int);
#endif
/////////////////////////////////////////
// CLASS IMPLEMENTATION /////////////////
/////////////////////////////////////////
// Begin app, called once at the start
void app::Begin( void )
{
// initialise the debug console
if (_USE_DEBUG_CONSOLE_) TADebugConsole::init_console("YourAppName","com.yourcompany.yourapp.syslog");
// assume okay for now
did_end = false;
app_state = _IN_START_INIT_;
memset(app_err_msg,0,4096);
// output a message (nothing happens if the init function not called)
TADebugConsole::send_to_console(TA_DEBUG_CONSOLE_DEBUG,"Starting display setup %.2",agk::Timer());
// do startup function to set up your display and get your splash or wait while loading
// does not do main app initialisation, just enough to get something up on the display
// prior to starting actual initialisation
if (doDisplaySetupAndSplash()) hadAnAppError("Failed to initialise properly:");
}
// Main loop, called every frame
void app::Loop ( void )
{
// check for ended
if (app::did_end) return;
// process based on state
switch (app_state)
{
case _IN_GAME_PLAY_:
// handle the main processing stuff
// NOTE: the 'doMainProcessingLoop' function is a dummy
// that is not provided in this sample code
switch (doMainProcessingLoop())
{
case 0:
// do something for main play;
break;
case 1:
// player pressed 'Exit'
// close down
closeThisApp();
// done, we want to make sure we skip the Sync() call
return;
case 2:
// an error occurred while starting a game
hadAnAppError("Game did not start properly:");
break;
case 3:
// an error occured while initialising options
hadAnAppError("Failed to initialise options:");
break;
}
break;
case _IN_BAD_STATE_:
// display message and done
inBadState();
break;
case _IN_START_INIT_:
// we want to make sure that the display syncs right now
// before we start initialisation
agk::Sync();
agk::Sync();
// start actual init next cycle
app_state = _IN_INITIALISATION_;
// output a message (nothing happens if the init function not called)
TADebugConsole::send_to_console(TA_DEBUG_CONSOLE_DEBUG,"Starting app initialisaton at %.2",agk::Timer());
break;
case _IN_INITIALISATION_:
// do initialisation
// NOTE: the 'initialiseTheGame' function is a dummy
// that is not provided in this sample code
if (initialiseTheGame()) hadAnAppError("Failed to initialise properly:");
else app_state = _IN_GAME_PLAY_;
break;
}
// sync display
agk::Sync();
}
// Called when the app ends
void app::End ( void )
{
// clean up, close debug output
TADebugConsole::close_console();
}
void app::closeThisApp(bool noforce/*=true*/)
{
// if in debug mode we want to leave the consoles displayed
if (noforce&&_USE_DEBUG_CONSOLE_)
{
// do a couple of sync calls to clear inputs
agk::Sync();
agk::Sync();
// indicate 'error', but it won't be visible
hadAnAppError("Waiting while showing DebugConsole");
return;
}
// indicate done
app::did_end = true;
// completely exit the app
#ifdef AGKWINDOWS
PostQuitMessage(0);
#endif
#ifdef AGKIOS
// forcing a quit in iOS is against recommended guidelines - use HOME button
// the exit button is disabled on AGKIOS builds
// but if you want to do so, this is the code
agk::MasterReset();
exit(0);
#endif
#ifdef IDE_ANDROID
// similar to iOS, an exit button should not be done
// but if you want to do so, this is the code
agk::MasterReset();
exit(0);
#endif
#ifdef IDE_MAC
glfwCloseWindow();
#endif
}
void app::checkForExit()
{
// if pointer pressed, we are done
if (ThePointer::gotPress()) closeThisApp(false);
}
void app::hadAnAppError(const char* errmsg)
{
// save the message
sprintf(app_err_msg,"\n\n\n%s\n\n%s\n\n\nTouch anywhere to exit",errmsg,ta_oops::get_the_oops());
// save the state as bad
app_state = _IN_BAD_STATE_;
// show a message
agk::Print(app_err_msg);
}
void app::inBadState()
{
// check for exit
app::checkForExit();
// show a message
agk::Print(app_err_msg);
}
Note that the functions mentioned in the sample code are fictitious unless they appear in the sample code displayed. The names are meant to suggest what they do.
Cheers,
Ancient Lady
AGK Community Tester and AppGameKit Master