/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
Copyright (C) 2024 Robert Beckebans
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see .
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "precompiled.h"
#pragma hdrstop
#include "../sys/sys_local.h"
#include "../framework/EventLoop.h"
#include "../framework/DeclManager.h"
#include
#include
#include "imtui/imtui.h"
#include "imtui/imtui-impl-ncurses.h"
#include "imtui/imtui-demo.h"
idEventLoop* eventLoop;
//-----------------------------------------------------------------------------
// [SECTION] Example App: Debug Log / ShowExampleAppLog()
//-----------------------------------------------------------------------------
// Usage:
// static ExampleAppLog my_log;
// my_log.AddLog("Hello %d world\n", 123);
// my_log.Draw("title");
struct MyAppLog
{
ImGuiTextBuffer Buf;
ImGuiTextFilter Filter;
ImVector LineOffsets; // Index to lines offset. We maintain this with AddLog() calls, allowing us to have a random access on lines
MyAppLog()
{
Clear();
}
void Clear()
{
Buf.clear();
LineOffsets.clear();
LineOffsets.push_back( 0 );
}
void AddLog( const char* fmt, ... ) IM_FMTARGS( 2 )
{
int old_size = Buf.size();
va_list args;
va_start( args, fmt );
Buf.appendfv( fmt, args );
va_end( args );
for( int new_size = Buf.size(); old_size < new_size; old_size++ )
if( Buf[old_size] == '\n' )
{
LineOffsets.push_back( old_size + 1 );
}
}
void Draw( const char* title, bool* p_open = NULL )
{
{
auto wSize = ImGui::GetIO().DisplaySize;
ImGui::SetNextWindowPos( ImVec2( 0, 1 ), ImGuiCond_Always );
ImGui::SetNextWindowSize( ImVec2( wSize.x, wSize.y - 5 ), ImGuiCond_Always );
}
if( !ImGui::Begin( title, p_open, ImGuiWindowFlags_NoDecoration ) )
{
ImGui::End();
return;
}
bool copy = ImGui::Button( "Copy to Clipboard" );
ImGui::SameLine();
ImGui::Separator();
ImGui::BeginChild( "scrolling", ImVec2( 0, 0 ), false, ImGuiWindowFlags_HorizontalScrollbar );
if( copy )
{
ImGui::LogToClipboard();
}
ImGui::PushStyleVar( ImGuiStyleVar_ItemSpacing, ImVec2( 0, 0 ) );
const char* buf = Buf.begin();
const char* buf_end = Buf.end();
#if 0
if( Filter.IsActive() )
{
// In this example we don't use the clipper when Filter is enabled.
// This is because we don't have a random access on the result on our filter.
// A real application processing logs with ten of thousands of entries may want to store the result of search/filter.
// especially if the filtering function is not trivial (e.g. reg-exp).
for( int line_no = 0; line_no < LineOffsets.Size; line_no++ )
{
const char* line_start = buf + LineOffsets[line_no];
const char* line_end = ( line_no + 1 < LineOffsets.Size ) ? ( buf + LineOffsets[line_no + 1] - 1 ) : buf_end;
if( Filter.PassFilter( line_start, line_end ) )
{
ImGui::TextUnformatted( line_start, line_end );
}
}
}
else
#endif
{
// The simplest and easy way to display the entire buffer:
// ImGui::TextUnformatted(buf_begin, buf_end);
// And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward to skip non-visible lines.
// Here we instead demonstrate using the clipper to only process lines that are within the visible area.
// If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them on your side is recommended.
// Using ImGuiListClipper requires A) random access into your data, and B) items all being the same height,
// both of which we can handle since we an array pointing to the beginning of each line of text.
// When using the filter (in the block of code above) we don't have random access into the data to display anymore, which is why we don't use the clipper.
// Storing or skimming through the search result would make it possible (and would be recommended if you want to search through tens of thousands of entries)
ImGuiListClipper clipper;
clipper.Begin( LineOffsets.Size );
while( clipper.Step() )
{
for( int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++ )
{
const char* line_start = buf + LineOffsets[line_no];
const char* line_end = ( line_no + 1 < LineOffsets.Size ) ? ( buf + LineOffsets[line_no + 1] - 1 ) : buf_end;
ImGui::TextUnformatted( line_start, line_end );
}
}
clipper.End();
}
ImGui::PopStyleVar();
//if( ImGui::GetScrollY() >= ImGui::GetScrollMaxY() )
{
ImGui::SetScrollHereY( 1.0f );
}
ImGui::EndChild();
ImGui::End();
}
};
static MyAppLog tuiLog;
#define MAXPRINTMSG 4096
#define STDIO_PRINT( pre, post ) \
char msg[MAXPRINTMSG]; \
va_list argptr; \
va_start( argptr, fmt ); \
idStr::vsnPrintf( msg, MAXPRINTMSG - 1, fmt, argptr ); \
msg[ sizeof( msg ) - 1 ] = '\0'; \
va_end( argptr ); \
Sys_DebugPrintf( "%s%s%s", pre, msg, post ); \
idCVar com_developer( "developer", "0", CVAR_BOOL | CVAR_SYSTEM, "developer mode" );
idCVar com_productionMode( "com_productionMode", "0", CVAR_SYSTEM | CVAR_BOOL, "0 - no special behavior, 1 - building a production build, 2 - running a production build" );
/*
==============================================================
idSys
==============================================================
*/
/*
==============
Sys_DebugPrintf
==============
*/
void Sys_DebugPrintf( const char* fmt, ... )
{
char msg[MAXPRINTMSG];
va_list argptr;
va_start( argptr, fmt );
idStr::vsnPrintf( msg, MAXPRINTMSG - 1, fmt, argptr );
msg[ sizeof( msg ) - 1 ] = '\0';
va_end( argptr );
printf( msg );
OutputDebugString( msg );
tuiLog.AddLog( "%s", msg );
}
/*
==============
Sys_DebugVPrintf
==============
*/
void Sys_DebugVPrintf( const char* fmt, va_list arg )
{
char msg[MAXPRINTMSG];
idStr::vsnPrintf( msg, MAXPRINTMSG - 1, fmt, arg );
msg[ sizeof( msg ) - 1 ] = '\0';
printf( msg );
OutputDebugString( msg );
tuiLog.AddLog( "%s", msg );
}
/*
==============
Sys_Mkdir
==============
*/
void Sys_Mkdir( const char* path )
{
_mkdir( path );
}
/*
========================
Sys_Rmdir
========================
*/
bool Sys_Rmdir( const char* path )
{
return _rmdir( path ) == 0;
}
/*
==============
Sys_EXEPath
==============
*/
const char* Sys_EXEPath()
{
static char exe[MAX_OSPATH];
GetModuleFileName( NULL, exe, sizeof( exe ) - 1 );
return exe;
}
/*
==============
Sys_ListFiles
==============
*/
int Sys_ListFiles( const char* directory, const char* extension, idStrList& list )
{
idStr search;
struct _finddata_t findinfo;
// RB: 64 bit fixes, changed int to intptr_t
intptr_t findhandle;
// RB end
int flag;
if( !extension )
{
extension = "";
}
// passing a slash as extension will find directories
if( extension[0] == '/' && extension[1] == 0 )
{
extension = "";
flag = 0;
}
else
{
flag = _A_SUBDIR;
}
sprintf( search, "%s\\*%s", directory, extension );
// search
list.Clear();
findhandle = _findfirst( search, &findinfo );
if( findhandle == -1 )
{
return -1;
}
do
{
if( flag ^ ( findinfo.attrib & _A_SUBDIR ) )
{
list.Append( findinfo.name );
}
}
while( _findnext( findhandle, &findinfo ) != -1 );
_findclose( findhandle );
return list.Num();
}
int idEventLoop::JournalLevel() const
{
return 0;
}
/*
========================
Sys_IsFolder
========================
*/
sysFolder_t Sys_IsFolder( const char* path )
{
struct _stat buffer;
if( _stat( path, &buffer ) < 0 )
{
return FOLDER_ERROR;
}
return ( buffer.st_mode & _S_IFDIR ) != 0 ? FOLDER_YES : FOLDER_NO;
}
const char* Sys_DefaultSavePath()
{
return "";
}
const char* Sys_Lang( int )
{
return "";
}
/*
=================
Sys_FileTimeStamp
=================
*/
ID_TIME_T Sys_FileTimeStamp( idFileHandle fp )
{
FILETIME writeTime;
GetFileTime( fp, NULL, NULL, &writeTime );
/*
FILETIME = number of 100-nanosecond ticks since midnight
1 Jan 1601 UTC. time_t = number of 1-second ticks since
midnight 1 Jan 1970 UTC. To translate, we subtract a
FILETIME representation of midnight, 1 Jan 1970 from the
time in question and divide by the number of 100-ns ticks
in one second.
*/
SYSTEMTIME base_st =
{
1970, // wYear
1, // wMonth
0, // wDayOfWeek
1, // wDay
0, // wHour
0, // wMinute
0, // wSecond
0 // wMilliseconds
};
FILETIME base_ft;
SystemTimeToFileTime( &base_st, &base_ft );
LARGE_INTEGER itime;
itime.QuadPart = reinterpret_cast( writeTime ).QuadPart;
itime.QuadPart -= reinterpret_cast( base_ft ).QuadPart;
itime.QuadPart /= 10000000LL;
return itime.QuadPart;
}
/*
================
Sys_GetClockTicks
================
*/
double Sys_GetClockTicks()
{
LARGE_INTEGER li;
QueryPerformanceCounter( &li );
return ( double ) li.LowPart + ( double ) 0xFFFFFFFF * li.HighPart;
}
/*
================
Sys_ClockTicksPerSecond
================
*/
double Sys_ClockTicksPerSecond()
{
static double ticks = 0;
if( !ticks )
{
LARGE_INTEGER li;
QueryPerformanceFrequency( &li );
ticks = li.QuadPart;
}
return ticks;
}
/*
==============
Sys_Cwd
==============
*/
const char* Sys_Cwd()
{
static char cwd[MAX_OSPATH];
_getcwd( cwd, sizeof( cwd ) - 1 );
cwd[MAX_OSPATH - 1] = 0;
return cwd;
}
/*
==============
Sys_DefaultBasePath
==============
*/
const char* Sys_DefaultBasePath()
{
return Sys_Cwd();
}
int Sys_NumLangs()
{
return 0;
}
/*
================
Sys_Milliseconds
================
*/
int Sys_Milliseconds()
{
static DWORD sys_timeBase = timeGetTime();
return timeGetTime() - sys_timeBase;
}
class idSysCmdline : public idSys
{
public:
virtual void DebugPrintf( VERIFY_FORMAT_STRING const char* fmt, ... )
{
va_list argptr;
va_start( argptr, fmt );
Sys_DebugVPrintf( fmt, argptr );
va_end( argptr );
}
virtual void DebugVPrintf( const char* fmt, va_list arg )
{
Sys_DebugVPrintf( fmt, arg );
}
virtual double GetClockTicks()
{
return Sys_GetClockTicks();
}
virtual double ClockTicksPerSecond()
{
return Sys_ClockTicksPerSecond();
}
virtual cpuid_t GetProcessorId()
{
return CPUID_NONE;
}
virtual const char* GetProcessorString()
{
return NULL;
}
virtual const char* FPU_GetState()
{
return NULL;
}
virtual bool FPU_StackIsEmpty()
{
return NULL;
}
virtual void FPU_SetFTZ( bool enable ) {}
virtual void FPU_SetDAZ( bool enable ) {}
virtual void FPU_EnableExceptions( int exceptions ) {}
virtual bool LockMemory( void* ptr, int bytes )
{
return false;
}
virtual bool UnlockMemory( void* ptr, int bytes )
{
return false;
}
virtual int DLL_Load( const char* dllName )
{
return 0;
}
virtual void* DLL_GetProcAddress( int dllHandle, const char* procName )
{
return NULL;
}
virtual void DLL_Unload( int dllHandle ) {}
virtual void DLL_GetFileName( const char* baseName, char* dllName, int maxLength ) {}
virtual sysEvent_t GenerateMouseButtonEvent( int button, bool down )
{
sysEvent_t ev;
ev.evType = SE_NONE;
return ev;
}
virtual sysEvent_t GenerateMouseMoveEvent( int deltax, int deltay )
{
sysEvent_t ev;
ev.evType = SE_NONE;
return ev;
}
virtual void OpenURL( const char* url, bool quit ) {}
virtual void StartProcess( const char* exeName, bool quit ) {}
};
idSysCmdline idSysLocal;
idSys* sys = &idSysLocal;
/*
==============================================================
idCommon
==============================================================
*/
/*
#include
#include