Console revamp - linux part.

develop
Petr Mrázek 2011-06-22 08:14:21 +02:00
parent b41ab40bb6
commit f8cdd95b09
12 changed files with 558 additions and 59 deletions

@ -13,7 +13,7 @@ include_directories (private)
SET(PROJECT_HDRS_INTERNAL
private/ContextShared.h
private/Internal.h
private/core.h
private/wdirent.h
)
SET(PROJECT_HDRS
@ -23,6 +23,7 @@ include/dfhack/Export.h
include/dfhack/MiscUtils.h
include/dfhack/Module.h
include/dfhack/Pragma.h
include/dfhack/Console.h
include/dfhack/Process.h
include/dfhack/TileTypes.h
include/dfhack/Types.h
@ -32,6 +33,7 @@ include/dfhack/VersionInfo.h
include/dfhack/extra/MapExtras.h
include/dfhack/extra/termutil.h
include/dfhack/extra/stopwatch.h
include/dfhack/extra/stdiostream.h
include/dfhack/modules/Buildings.h
include/dfhack/modules/Constructions.h
include/dfhack/modules/Creatures.h
@ -80,11 +82,13 @@ SET(PROJECT_HDRS_WINDOWS
SET(PROJECT_SRCS_LINUX
Core-linux.cpp
Console-linux.cpp
Process-linux.cpp
)
SET(PROJECT_SRCS_WINDOWS
Core-windows.cpp
Console-windows.cpp
Process-windows.cpp
)

@ -0,0 +1,119 @@
/*
https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#include "dfhack/Console.h"
#include <cstdio>
#include <cstdlib>
#include <sstream>
using namespace DFHack;
duthomhas::stdiostream dfout;
FILE * dfout_C = 0;
duthomhas::stdiobuf * stream_o = 0;
// FIXME: prime candidate for being a singleton...
Console::Console()
{
// make our own weird streams so our IO isn't redirected
dfout_C = fopen("/dev/tty", "w");
stream_o = new duthomhas::stdiobuf(dfout_C);
dfout.rdbuf(stream_o);
std::cin.tie(&dfout);
clear();
// result is a terminal controlled by the parasitic code!
}
Console::~Console()
{
}
void Console::clear()
{
dfout << "\033c";
dfout << "\033[3J\033[H";
}
void Console::gotoxy(int x, int y)
{
std::ostringstream oss;
oss << "\033[" << y << ";" << x << "H";
dfout << oss.str();
}
const char * ANSI_CLS = "\033[2J";
const char * ANSI_BLACK = "\033[22;30m";
const char * ANSI_RED = "\033[22;31m";
const char * ANSI_GREEN = "\033[22;32m";
const char * ANSI_BROWN = "\033[22;33m";
const char * ANSI_BLUE = "\033[22;34m";
const char * ANSI_MAGENTA = "\033[22;35m";
const char * ANSI_CYAN = "\033[22;36m";
const char * ANSI_GREY = "\033[22;37m";
const char * ANSI_DARKGREY = "\033[01;30m";
const char * ANSI_LIGHTRED = "\033[01;31m";
const char * ANSI_LIGHTGREEN = "\033[01;32m";
const char * ANSI_YELLOW = "\033[01;33m";
const char * ANSI_LIGHTBLUE = "\033[01;34m";
const char * ANSI_LIGHTMAGENTA = "\033[01;35m";
const char * ANSI_LIGHTCYAN = "\033[01;36m";
const char * ANSI_WHITE = "\033[01;37m";
const char * getANSIColor(const int c)
{
switch (c)
{
case 0 : return ANSI_BLACK;
case 1 : return ANSI_BLUE; // non-ANSI
case 2 : return ANSI_GREEN;
case 3 : return ANSI_CYAN; // non-ANSI
case 4 : return ANSI_RED; // non-ANSI
case 5 : return ANSI_MAGENTA;
case 6 : return ANSI_BROWN;
case 7 : return ANSI_GREY;
case 8 : return ANSI_DARKGREY;
case 9 : return ANSI_LIGHTBLUE; // non-ANSI
case 10: return ANSI_LIGHTGREEN;
case 11: return ANSI_LIGHTCYAN; // non-ANSI;
case 12: return ANSI_LIGHTRED; // non-ANSI;
case 13: return ANSI_LIGHTMAGENTA;
case 14: return ANSI_YELLOW; // non-ANSI
case 15: return ANSI_WHITE;
default: return "";
}
}
void Console::color(int index)
{
dfout << getANSIColor(index);
}
void Console::cursor(bool enable)
{
if(enable)
{
dfout <<"\033[?25h";
}
else
{
dfout <<"\033[?25l";
}
}

@ -46,20 +46,22 @@ distribution.
/*
* Plugin loading functions
*/
DFLibrary * OpenPlugin (const char * filename)
namespace DFHack
{
return (DFLibrary *) dlopen(filename, RTLD_NOW);
}
void * LookupPlugin (DFLibrary * plugin ,const char * function)
{
return (DFLibrary *) dlsym((void *)plugin, function);
}
void ClosePlugin (DFLibrary * plugin)
{
dlclose((void *) plugin);
DFLibrary * OpenPlugin (const char * filename)
{
return (DFLibrary *) dlopen(filename, RTLD_NOW);
}
void * LookupPlugin (DFLibrary * plugin ,const char * function)
{
return (DFLibrary *) dlsym((void *)plugin, function);
}
void ClosePlugin (DFLibrary * plugin)
{
dlclose((void *) plugin);
}
}
/*******************************************************************************
* SDL part starts here *
*******************************************************************************/
@ -166,6 +168,8 @@ DFhackCExport void SDL_Quit(void)
// hook - called at program start, initialize some stuffs we'll use later
DFhackCExport int SDL_Init(uint32_t flags)
{
freopen("stdout.log", "w", stdout);
freopen("stderr.log", "w", stderr);
// find real functions
//_SDL_GL_SwapBuffers = (void (*)( void )) dlsym(RTLD_NEXT, "SDL_GL_SwapBuffers");
_SDL_Init = (int (*)( uint32_t )) dlsym(RTLD_NEXT, "SDL_Init");

@ -115,19 +115,21 @@ void RedirectIOToConsole()
/*
* Plugin loading functions
*/
DFLibrary * OpenPlugin (const char * filename)
namespace DFHack
{
return (DFLibrary *) LoadLibrary(filename);
}
void * LookupPlugin (DFLibrary * plugin ,const char * function)
{
return (DFLibrary *) GetProcAddress((HMODULE)plugin, function);
}
void ClosePlugin (DFLibrary * plugin)
{
FreeLibrary((HMODULE) plugin);
DFLibrary * OpenPlugin (const char * filename)
{
return (DFLibrary *) LoadLibrary(filename);
}
void * LookupPlugin (DFLibrary * plugin ,const char * function)
{
return (DFLibrary *) GetProcAddress((HMODULE)plugin, function);
}
void ClosePlugin (DFLibrary * plugin)
{
FreeLibrary((HMODULE) plugin);
}
}
/*************************************************************************/
// extremely boring wrappers beyond this point. Only fix when broken

@ -33,6 +33,7 @@ distribution.
using namespace std;
#include "dfhack/Core.h"
#include "dfhack/Console.h"
#include "dfhack/VersionInfoFactory.h"
#include "ModuleFactory.h"
#include "dfhack/Error.h"
@ -57,7 +58,7 @@ static int getdir (string dir, vector<string> &files)
struct dirent *dirp;
if((dp = opendir(dir.c_str())) == NULL)
{
cout << "Error(" << errno << ") opening " << dir << endl;
dfout << "Error(" << errno << ") opening " << dir << endl;
return errno;
}
while ((dirp = readdir(dp)) != NULL) {
@ -83,47 +84,52 @@ int fIOthread(void * _core)
DFLibrary * plug = OpenPlugin(filez[i].c_str());
if(!plug)
{
cerr << "Can't load plugin " << filez[i] << endl;
dfout << "Can't load plugin " << filez[i] << endl;
continue;
}
_PlugName = (const char * (*)()) LookupPlugin(plug, "plugin_name");
if(!_PlugName)
{
cerr << "Plugin " << filez[i] << " has no name." << endl;
dfout << "Plugin " << filez[i] << " has no name." << endl;
ClosePlugin(plug);
continue;
}
_PlugRun = (int (*)(Core * c)) LookupPlugin(plug, "plugin_run");
if(!_PlugRun)
{
cerr << "Plugin " << filez[i] << " has no run function." << endl;
dfout << "Plugin " << filez[i] << " has no run function." << endl;
ClosePlugin(plug);
continue;
}
cout << filez[i] << endl;
dfout << filez[i] << endl;
plugins[string(_PlugName())] = _PlugRun;
}
}
cout << "Hello from the IO thread. Have a nice day!" << endl;
fprintf(dfout_C,"DFHack is ready. Have a nice day! Type in '?' or 'help' for help.\n");
//dfterm << << endl;
int clueless_counter = 0;
while (true)
{
string command = "";
cout <<"[DFHack]# ";
dfout <<"[DFHack]# ";
getline(cin, command);
if (std::cin.eof())
if (cin.eof())
{
command = "q";
std::cout << std::endl; // No newline from the user here!
dfout << std::endl; // No newline from the user here!
}
if(command=="help" || command == "?")
{
cout << "Available commands:" << endl;
dfout << "Available commands:" << endl;
for (map <string, int (*)(Core *)>::iterator iter = plugins.begin(); iter != plugins.end(); iter++)
{
cout << iter->first << endl;
dfout << iter->first << endl;
}
}
// TODO: commands will be registered. We'll scan a map of command -> function pointer and call stuff.
else if( command == "" )
{
clueless_counter++;
}
else
{
map <string, int (*)(Core *)>::iterator iter = plugins.find(command);
@ -133,20 +139,28 @@ int fIOthread(void * _core)
}
else
{
cout << "Do 'help' or '?' for the list of available commands." << endl;
dfout << "Invalid command." << endl;
clueless_counter ++;
}
}
if(clueless_counter == 3)
{
dfout << "Do 'help' or '?' for the list of available commands." << endl;
clueless_counter = 0;
}
}
}
Core::Core()
{
// init the console. This must be always the first step!
con = new Console();
// find out what we are...
vif = new DFHack::VersionInfoFactory("Memory.xml");
p = new DFHack::Process(vif);
if (!p->isIdentified())
{
std::cerr << "Couldn't identify this version of DF." << std::endl;
dfout << "Couldn't identify this version of DF." << std::endl;
errorstate = true;
delete p;
p = NULL;
@ -162,7 +176,7 @@ Core::Core()
AccessMutex = SDL_CreateMutex();
if(!AccessMutex)
{
std::cerr << "Mutex creation failed." << std::endl;
dfout << "Mutex creation failed." << std::endl;
errorstate = true;
return;
}
@ -205,6 +219,8 @@ int Core::Update()
int Core::Shutdown ( void )
{
errorstate = 1;
// TODO:shutdown all plugins
// invalidate all modules
for(unsigned int i = 0 ; i < allModules.size(); i++)
{
@ -212,7 +228,7 @@ int Core::Shutdown ( void )
}
allModules.clear();
memset(&(s_mods), 0, sizeof(s_mods));
// maybe do more
dfout << std::endl;
return -1;
}

@ -0,0 +1,46 @@
/*
https://github.com/peterix/dfhack
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#pragma once
#include "dfhack/Pragma.h"
#include "dfhack/Export.h"
#include "dfhack/Core.h"
#include "dfhack/extra/stdiostream.h"
extern DFHACK_EXPORT duthomhas::stdiostream dfout;
extern DFHACK_EXPORT FILE * dfout_C;
namespace DFHack
{
class DFHACK_EXPORT Console
{
public:
Console();
~Console();
void clear();
void gotoxy(int x, int y);
void color(int index);
void cursor(bool enable = true);
};
}

@ -47,6 +47,11 @@ namespace DFHack
class Constructions;
class VersionInfo;
class VersionInfoFactory;
class Console;
DFLibrary * OpenPlugin (const char * filename);
void * LookupPlugin (DFLibrary * plugin ,const char * function);
void ClosePlugin (DFLibrary * plugin);
// Core is a singleton. Why? Because it is closely tied to SDL calls. It tracks the global state of DF.
// There should never be more than one instance
@ -95,6 +100,7 @@ namespace DFHack
DFHack::Process * p;
DFHack::VersionInfo * vinfo;
Console * con;
private:
Core();
int Update (void);

@ -51,10 +51,6 @@ DFhackCExport void * SDL_LoadFunction(DFLibrary *handle, const char *name);
DFhackCExport DFLibrary * SDL_LoadObject(const char *sofile);
DFhackCExport void SDL_UnloadObject(DFLibrary * handle);
DFLibrary * OpenPlugin (const char * filename);
void * LookupPlugin (DFLibrary * plugin ,const char * function);
void ClosePlugin (DFLibrary * plugin);
// these functions are here because they call into DFHack::Core and therefore need to
// be declared as friend functions/known
DFhackCExport int SDL_NumJoysticks(void);

@ -0,0 +1,302 @@
// stdiostream.hpp
//
// Copyright (c) 2010 Michael Thomas Greer
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt )
//
#pragma once
#ifndef DUTHOMHAS_STDIOSTREAM_HPP
#define DUTHOMHAS_STDIOSTREAM_HPP
#include <cstdio>
#include <iostream>
#include <streambuf>
#include <string>
#include <vector>
namespace duthomhas
{
/* /////////////////////////////////////////////////////////////////////////
basic_stdiobuf
///////////////////////////////////////////////////////////////////////// */
template <
typename CharType,
typename CharTraits = std::char_traits <CharType>
>
class basic_stdiobuf: public std::basic_streambuf <CharType, CharTraits>
{
//------------------------------------------------------------------------
public:
//------------------------------------------------------------------------
typedef CharType char_type;
typedef CharTraits traits_type;
typedef typename traits_type::int_type int_type;
typedef typename traits_type::pos_type pos_type;
typedef typename traits_type::off_type off_type;
typedef basic_stdiobuf <char_type, traits_type> this_type;
//......................................................................
basic_stdiobuf( FILE* fp = NULL ):
fp( fp )
{ }
//......................................................................
//BUG 1: Hey! I never get called! (How is that?)
~basic_stdiobuf()
{
this->close();
}
//......................................................................
bool is_open() const throw()
{
return fp != NULL;
}
//......................................................................
this_type* open( const char* filename, std::ios_base::openmode mode )
{
if (is_open()) return NULL;
// Figure out the open mode flags . . . . . . . . . . . . . . . . . .
std::string fmode;
bool is_ate = mode & std::ios_base::ate;
bool is_bin = mode & std::ios_base::binary;
mode &= ~(std::ios_base::ate | std::ios_base::binary);
#define _(flag) std::ios_base::flag
if (mode == ( _(in) )) fmode = "r";
else if (mode == ( _(out) & _(trunc))) fmode = "w";
else if (mode == (_(app) & _(out) )) fmode = "a";
else if (mode == ( _(in) & _(out) )) fmode = "r+";
else if (mode == ( _(in) & _(out) & _(trunc))) fmode = "w+";
else if (mode == (_(app) & _(in) & _(out) )) fmode = "a+";
// I would prefer to throw an exception here,
// but the standard only wants a NULL result.
else return NULL;
#undef _
if (is_bin) fmode.insert( 1, 1, 'b' );
// Try opening the file . . . . . . . . . . . . . . . . . . . . . . .
fp = std::fopen( filename, fmode.c_str() );
if (!fp) return NULL;
// Reposition to EOF if wanted . . . . . . . . . . . . . . . . . . . .
if (is_ate) std::fseek( fp, 0, SEEK_END );
return this;
}
//......................................................................
this_type* close()
{
if (fp)
{
std::fclose( fp );
fp = NULL;
}
pushbacks.clear();
return this;
}
//......................................................................
FILE* stdiofile() const
{
return fp;
}
//------------------------------------------------------------------------
protected:
//------------------------------------------------------------------------
//......................................................................
// Get the CURRENT character without advancing the file pointer
virtual int_type underflow()
{
// Return anything previously pushed-back
if (pushbacks.size())
return pushbacks.back();
// Else do the right thing
fpos_t pos;
if (std::fgetpos( fp, &pos ) != 0)
return traits_type::eof();
int c = std::fgetc( fp );
std::fsetpos( fp, &pos );
return maybe_eof( c );
}
//......................................................................
// Get the CURRENT character AND advance the file pointer
virtual int_type uflow()
{
// Return anything previously pushed-back
if (pushbacks.size())
{
int_type c = pushbacks.back();
pushbacks.pop_back();
return c;
}
// Else do the right thing
return maybe_eof( std::fgetc( fp ) );
}
//......................................................................
virtual int_type pbackfail( int_type c = traits_type::eof() )
{
if (!is_open())
return traits_type::eof();
// If the argument c is EOF and the file pointer is not at the
// beginning of the character sequence, it is decremented by one.
if (traits_type::eq_int_type( c, traits_type::eof() ))
{
pushbacks.clear();
return std::fseek( fp, -1L, SEEK_CUR )
? traits_type::eof()
: 0;
}
// Otherwise, make the argument the next value to be returned by
// underflow() or uflow()
pushbacks.push_back( c );
return c;
}
//......................................................................
virtual int_type overflow( int_type c = traits_type::eof() )
{
pushbacks.clear();
// Do nothing
if (traits_type::eq_int_type( c, traits_type::eof() ))
return 0;
// Else write a character
return maybe_eof( std::fputc( c, fp ) );
}
//......................................................................
virtual this_type* setbuf( char* s, std::streamsize n )
{
return std::setvbuf( fp, s, (s and n) ? _IOLBF : _IONBF, (size_t)n )
? NULL
: this;
}
//......................................................................
virtual pos_type seekoff(
off_type offset,
std::ios_base::seekdir direction,
std::ios_base::openmode which = std::ios_base::in | std::ios_base::out
) {
pushbacks.clear();
return std::fseek( fp, offset,
(direction == std::ios_base::beg) ? SEEK_SET :
(direction == std::ios_base::cur) ? SEEK_CUR :
SEEK_END
) ? (-1) : std::ftell( fp );
}
//......................................................................
virtual pos_type seekpos(
pos_type position,
std::ios_base::openmode which = std::ios_base::in | std::ios_base::out
) {
pushbacks.clear();
return std::fseek( fp, position, SEEK_SET )
? (-1)
: std::ftell( fp );
}
//......................................................................
virtual int sync()
{
pushbacks.clear();
return std::fflush( fp )
? traits_type::eof()
: 0;
}
//------------------------------------------------------------------------
private:
//------------------------------------------------------------------------
FILE* fp;
std::vector <int_type> pushbacks; // we'll treat this like a stack
//......................................................................
// Utility function to make sure EOF gets translated to the proper value
inline int_type maybe_eof( int value ) const
{
return (value == EOF) ? traits_type::eof() : value;
}
};
/* /////////////////////////////////////////////////////////////////////////
basic_stdiostream
///////////////////////////////////////////////////////////////////////// */
template <
typename CharType,
typename CharTraits = std::char_traits <CharType>
>
struct basic_stdiostream: public std::basic_iostream <CharType, CharTraits>
{
typedef CharType char_type;
typedef CharTraits traits_type;
typedef basic_stdiobuf <char_type, traits_type> sbuf_type;
typedef basic_stdiostream <char_type, traits_type> this_type;
typedef std::basic_iostream <char_type, traits_type> base_type;
//......................................................................
basic_stdiostream( FILE* fp = NULL ):
base_type( new sbuf_type( fp ) )
{ }
//......................................................................
basic_stdiostream( const char* filename, std::ios_base::openmode mode ):
//BUG 2: Oops! This is a potential memory leak!
base_type( (new sbuf_type)->open( filename, mode ) )
{ }
//......................................................................
void open(
const char* filename,
std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out
) {
sbuf_type* buf = static_cast <sbuf_type*> ( this->rdbuf() );
if (!(buf->open( filename, mode )))
this->setstate( std::ios_base::badbit );
}
//......................................................................
void close()
{
sbuf_type* buf = static_cast <sbuf_type*> ( this->rdbuf() );
buf->close();
}
};
/* /////////////////////////////////////////////////////////////////////////
Useful typedefs
///////////////////////////////////////////////////////////////////////// */
typedef basic_stdiobuf <char> stdiobuf;
typedef basic_stdiostream <char> stdiostream;
} // namespace duthomhas
#endif
// end stdiostream.hpp

@ -1,4 +1,5 @@
#include <dfhack/Core.h>
#include <dfhack/Console.h>
#include <dfhack/Export.h>
#include <dfhack/extra/rlutil.h>
@ -9,6 +10,7 @@ DFhackCExport const char * plugin_name ( void )
DFhackCExport int plugin_run (DFHack::Core * c)
{
DFHack::Console * con = c->con;
const char * kittenz1 []=
{
" ____",
@ -21,25 +23,26 @@ DFhackCExport int plugin_run (DFHack::Core * c)
" '.,,/'.,,mrf",
0
};
rlutil::hidecursor();
rlutil::cls();
con->cursor(false);
con->clear();
int color = 1;
while(1)
{
rlutil::setColor(color);
con->color(color);
int index = 0;
const char * kit = kittenz1[index];
rlutil::locate(1,1);
std::cout << "Your DF is now full of kittens!" << std::endl;
con->gotoxy(1,1);
dfout << "Your DF is now full of kittens!" << std::endl;
while (kit != 0)
{
rlutil::locate(5,5+index);
std::cout << kit;
con->gotoxy(5,5+index);
dfout << kit;
index++;
kit = kittenz1[index];
}
std::fflush(stdout);
rlutil::msleep(60);
dfout.flush();
rlutil::msleep(60); // FIXME: replace!
con->clear();
color ++;
if(color > 15)
color = 1;

@ -3,6 +3,7 @@
#include <map>
#include <vector>
#include <dfhack/Core.h>
#include <dfhack/Console.h>
#include <dfhack/Export.h>
#include <dfhack/modules/Maps.h>
#include <dfhack/modules/World.h>
@ -29,12 +30,12 @@ DFhackCExport int plugin_run (DFHack::Core * c)
// init the map
if(!Maps->Start())
{
std::cerr << "Can't init map." << std::endl;
dfout << "Can't init map." << std::endl;
c->Resume();
return 1;
}
std::cout << "Revealing, please wait..." << std::endl;
dfout << "Revealing, please wait..." << std::endl;
uint32_t x_max, y_max, z_max;
DFHack::designations40d designations;
@ -70,12 +71,12 @@ DFhackCExport int plugin_run (DFHack::Core * c)
}
World->SetPauseState(true);
c->Resume();
std::cout << "Map revealed. The game has been paused for you." << std::endl;
std::cout << "Unpausing can unleash the forces of hell!" << std::endl;
std::cout << "Saving will make this state permanent. Don't do it." << std::endl << std::endl;
std::cout << "Press any key to unreveal." << std::endl;
dfout << "Map revealed. The game has been paused for you." << std::endl;
dfout << "Unpausing can unleash the forces of hell!" << std::endl;
dfout << "Saving will make this state permanent. Don't do it." << std::endl << std::endl;
dfout << "Press any key to unreveal." << std::endl;
std::cin.ignore();
std::cout << "Unrevealing... please wait." << std::endl;
dfout << "Unrevealing... please wait." << std::endl;
// FIXME: do some consistency checks here!
c->Suspend();
Maps = c->getMaps();