commit
52f9fe4a6a
@ -0,0 +1,773 @@
|
||||
/*
|
||||
https://github.com/peterix/dfhack
|
||||
|
||||
A thread-safe logging console with a line editor.
|
||||
|
||||
Based on linenoise:
|
||||
linenoise -- guerrilla line editing library against the idea that a
|
||||
line editing lib needs to be 20,000 lines of C code.
|
||||
|
||||
You can find the latest source code at:
|
||||
|
||||
http://github.com/antirez/linenoise
|
||||
|
||||
Does a number of crazy assumptions that happen to be true in 99.9999% of
|
||||
the 2010 UNIX computers around.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
Copyright (c) 2011, Petr Mrázek <peterix@gmail.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#include <errno.h>
|
||||
#include <deque>
|
||||
|
||||
// George Vulov for MacOSX
|
||||
#ifndef __LINUX__
|
||||
#define TEMP_FAILURE_RETRY(expr) \
|
||||
({ long int _res; \
|
||||
do _res = (long int) (expr); \
|
||||
while (_res == -1L && errno == EINTR); \
|
||||
_res; })
|
||||
#endif
|
||||
|
||||
#include "Console.h"
|
||||
#include "Hooks.h"
|
||||
using namespace DFHack;
|
||||
|
||||
#include "tinythread.h"
|
||||
using namespace tthread;
|
||||
|
||||
static int isUnsupportedTerm(void)
|
||||
{
|
||||
static const char *unsupported_term[] = {"dumb","cons25",NULL};
|
||||
char *term = getenv("TERM");
|
||||
int j;
|
||||
|
||||
if (term == NULL) return 0;
|
||||
for (j = 0; unsupported_term[j]; j++)
|
||||
if (!strcasecmp(term,unsupported_term[j])) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 * RESETCOLOR = "\033[0m";
|
||||
|
||||
const char * getANSIColor(const int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case -1: return RESETCOLOR; // HACK! :P
|
||||
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 "";
|
||||
}
|
||||
}
|
||||
|
||||
namespace DFHack
|
||||
{
|
||||
class Private
|
||||
{
|
||||
public:
|
||||
Private()
|
||||
{
|
||||
dfout_C = NULL;
|
||||
rawmode = false;
|
||||
in_batch = false;
|
||||
supported_terminal = false;
|
||||
state = con_unclaimed;
|
||||
};
|
||||
virtual ~Private()
|
||||
{
|
||||
//sync();
|
||||
}
|
||||
private:
|
||||
bool read_char(unsigned char & out)
|
||||
{
|
||||
FD_ZERO(&descriptor_set);
|
||||
FD_SET(STDIN_FILENO, &descriptor_set);
|
||||
FD_SET(exit_pipe[0], &descriptor_set);
|
||||
int ret = TEMP_FAILURE_RETRY(
|
||||
select (FD_SETSIZE,&descriptor_set, NULL, NULL, NULL)
|
||||
);
|
||||
if(ret == -1)
|
||||
return false;
|
||||
if (FD_ISSET(exit_pipe[0], &descriptor_set))
|
||||
return false;
|
||||
if (FD_ISSET(STDIN_FILENO, &descriptor_set))
|
||||
{
|
||||
// read byte from stdin
|
||||
ret = TEMP_FAILURE_RETRY(
|
||||
read(STDIN_FILENO, &out, 1)
|
||||
);
|
||||
if(ret == -1)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
void print(const char *data)
|
||||
{
|
||||
fputs(data, dfout_C);
|
||||
}
|
||||
|
||||
void print_text(color_ostream::color_value clr, const std::string &chunk)
|
||||
{
|
||||
if(!in_batch && state == con_lineedit)
|
||||
{
|
||||
disable_raw();
|
||||
fprintf(dfout_C,"\x1b[1G");
|
||||
fprintf(dfout_C,"\x1b[0K");
|
||||
|
||||
color(clr);
|
||||
print(chunk.c_str());
|
||||
|
||||
reset_color();
|
||||
enable_raw();
|
||||
prompt_refresh();
|
||||
}
|
||||
else
|
||||
{
|
||||
color(clr);
|
||||
print(chunk.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void begin_batch()
|
||||
{
|
||||
assert(!in_batch);
|
||||
|
||||
in_batch = true;
|
||||
|
||||
if (state == con_lineedit)
|
||||
{
|
||||
disable_raw();
|
||||
fprintf(dfout_C,"\x1b[1G");
|
||||
fprintf(dfout_C,"\x1b[0K");
|
||||
}
|
||||
}
|
||||
|
||||
void end_batch()
|
||||
{
|
||||
assert(in_batch);
|
||||
|
||||
flush();
|
||||
|
||||
in_batch = false;
|
||||
|
||||
if (state == con_lineedit)
|
||||
{
|
||||
reset_color();
|
||||
enable_raw();
|
||||
prompt_refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void flush()
|
||||
{
|
||||
if (!rawmode)
|
||||
fflush(dfout_C);
|
||||
}
|
||||
|
||||
/// Clear the console, along with its scrollback
|
||||
void clear()
|
||||
{
|
||||
if(rawmode)
|
||||
{
|
||||
const char * clr = "\033c\033[3J\033[H";
|
||||
::write(STDIN_FILENO,clr,strlen(clr));
|
||||
}
|
||||
else
|
||||
{
|
||||
print("\033c\033[3J\033[H");
|
||||
fflush(dfout_C);
|
||||
}
|
||||
}
|
||||
/// Position cursor at x,y. 1,1 = top left corner
|
||||
void gotoxy(int x, int y)
|
||||
{
|
||||
char tmp[64];
|
||||
sprintf(tmp,"\033[%d;%dH", y,x);
|
||||
print(tmp);
|
||||
}
|
||||
/// Set color (ANSI color number)
|
||||
void color(Console::color_value index)
|
||||
{
|
||||
if(!rawmode)
|
||||
fprintf(dfout_C,getANSIColor(index));
|
||||
else
|
||||
{
|
||||
const char * colstr = getANSIColor(index);
|
||||
int lstr = strlen(colstr);
|
||||
::write(STDIN_FILENO,colstr,lstr);
|
||||
}
|
||||
}
|
||||
/// Reset color to default
|
||||
void reset_color(void)
|
||||
{
|
||||
color(Console::COLOR_RESET);
|
||||
if(!rawmode)
|
||||
fflush(dfout_C);
|
||||
}
|
||||
/// Enable or disable the caret/cursor
|
||||
void cursor(bool enable = true)
|
||||
{
|
||||
if(enable)
|
||||
print("\033[?25h");
|
||||
else
|
||||
print("\033[?25l");
|
||||
}
|
||||
/// Waits given number of milliseconds before continuing.
|
||||
void msleep(unsigned int msec);
|
||||
/// get the current number of columns
|
||||
int get_columns(void)
|
||||
{
|
||||
winsize ws;
|
||||
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) return 80;
|
||||
return ws.ws_col;
|
||||
}
|
||||
/// get the current number of rows
|
||||
int get_rows(void)
|
||||
{
|
||||
winsize ws;
|
||||
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) return 25;
|
||||
return ws.ws_row;
|
||||
}
|
||||
/// beep. maybe?
|
||||
//void beep (void);
|
||||
/// A simple line edit (raw mode)
|
||||
int lineedit(const std::string& prompt, std::string& output, recursive_mutex * lock, CommandHistory & ch)
|
||||
{
|
||||
output.clear();
|
||||
reset_color();
|
||||
this->prompt = prompt;
|
||||
if (!supported_terminal)
|
||||
{
|
||||
print(prompt.c_str());
|
||||
fflush(dfout_C);
|
||||
// FIXME: what do we do here???
|
||||
//SDL_recursive_mutexV(lock);
|
||||
std::getline(std::cin, output);
|
||||
//SDL_recursive_mutexP(lock);
|
||||
return output.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
int count;
|
||||
if (enable_raw() == -1) return 0;
|
||||
if(state == con_lineedit)
|
||||
return -1;
|
||||
state = con_lineedit;
|
||||
count = prompt_loop(lock,ch);
|
||||
state = con_unclaimed;
|
||||
disable_raw();
|
||||
print("\n");
|
||||
if(count != -1)
|
||||
{
|
||||
output = raw_buffer;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
int enable_raw()
|
||||
{
|
||||
struct termios raw;
|
||||
|
||||
if (!supported_terminal)
|
||||
return -1;
|
||||
if (tcgetattr(STDIN_FILENO,&orig_termios) == -1)
|
||||
return -1;
|
||||
|
||||
raw = orig_termios; //modify the original mode
|
||||
// input modes: no break, no CR to NL, no parity check, no strip char,
|
||||
// no start/stop output control.
|
||||
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
|
||||
// output modes - disable post processing
|
||||
raw.c_oflag &= ~(OPOST);
|
||||
// control modes - set 8 bit chars
|
||||
raw.c_cflag |= (CS8);
|
||||
// local modes - choing off, canonical off, no extended functions,
|
||||
// no signal chars (^Z,^C)
|
||||
#ifdef CONSOLE_NO_CATCH
|
||||
raw.c_lflag &= ~( ECHO | ICANON | IEXTEN );
|
||||
#else
|
||||
raw.c_lflag &= ~( ECHO | ICANON | IEXTEN | ISIG );
|
||||
#endif
|
||||
// control chars - set return condition: min number of bytes and timer.
|
||||
// We want read to return every single byte, without timeout.
|
||||
raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0;// 1 byte, no timer
|
||||
// put terminal in raw mode after flushing
|
||||
if (tcsetattr(STDIN_FILENO,TCSAFLUSH,&raw) < 0)
|
||||
return -1;
|
||||
rawmode = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void disable_raw()
|
||||
{
|
||||
/* Don't even check the return value as it's too late. */
|
||||
if (rawmode && tcsetattr(STDIN_FILENO,TCSAFLUSH,&orig_termios) != -1)
|
||||
rawmode = 0;
|
||||
}
|
||||
void prompt_refresh()
|
||||
{
|
||||
char seq[64];
|
||||
int cols = get_columns();
|
||||
int plen = prompt.size();
|
||||
const char * buf = raw_buffer.c_str();
|
||||
int len = raw_buffer.size();
|
||||
int cooked_cursor = raw_cursor;
|
||||
// Use math! This is silly.
|
||||
while((plen+cooked_cursor) >= cols)
|
||||
{
|
||||
buf++;
|
||||
len--;
|
||||
cooked_cursor--;
|
||||
}
|
||||
while (plen+len > cols)
|
||||
{
|
||||
len--;
|
||||
}
|
||||
/* Cursor to left edge */
|
||||
snprintf(seq,64,"\x1b[1G");
|
||||
if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return;
|
||||
/* Write the prompt and the current buffer content */
|
||||
if (::write(STDIN_FILENO,prompt.c_str(),plen) == -1) return;
|
||||
if (::write(STDIN_FILENO,buf,len) == -1) return;
|
||||
/* Erase to right */
|
||||
snprintf(seq,64,"\x1b[0K");
|
||||
if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return;
|
||||
/* Move cursor to original position. */
|
||||
snprintf(seq,64,"\x1b[1G\x1b[%dC", (int)(cooked_cursor+plen));
|
||||
if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return;
|
||||
}
|
||||
|
||||
int prompt_loop(recursive_mutex * lock, CommandHistory & history)
|
||||
{
|
||||
int fd = STDIN_FILENO;
|
||||
size_t plen = prompt.size();
|
||||
int history_index = 0;
|
||||
raw_buffer.clear();
|
||||
raw_cursor = 0;
|
||||
/* The latest history entry is always our current buffer, that
|
||||
* initially is just an empty string. */
|
||||
const std::string empty;
|
||||
history.add(empty);
|
||||
if (::write(fd,prompt.c_str(),prompt.size()) == -1) return -1;
|
||||
while(1)
|
||||
{
|
||||
unsigned char c;
|
||||
int isok;
|
||||
unsigned char seq[2], seq2;
|
||||
lock->unlock();
|
||||
if(!read_char(c))
|
||||
{
|
||||
lock->lock();
|
||||
return -2;
|
||||
}
|
||||
lock->lock();
|
||||
/* Only autocomplete when the callback is set. It returns < 0 when
|
||||
* there was an error reading from fd. Otherwise it will return the
|
||||
* character that should be handled next. */
|
||||
if (c == 9)
|
||||
{
|
||||
/*
|
||||
if( completionCallback != NULL) {
|
||||
c = completeLine(fd,prompt,buf,buflen,&len,&pos,cols);
|
||||
// Return on errors
|
||||
if (c < 0) return len;
|
||||
// Read next character when 0
|
||||
if (c == 0) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ignore tab
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
// just ignore tabs
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(c)
|
||||
{
|
||||
case 13: // enter
|
||||
history.remove();
|
||||
return raw_buffer.size();
|
||||
case 3: // ctrl-c
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
case 127: // backspace
|
||||
case 8: // ctrl-h
|
||||
if (raw_cursor > 0 && raw_buffer.size() > 0)
|
||||
{
|
||||
raw_buffer.erase(raw_cursor-1,1);
|
||||
raw_cursor--;
|
||||
prompt_refresh();
|
||||
}
|
||||
break;
|
||||
case 27: // escape sequence
|
||||
lock->unlock();
|
||||
if(!read_char(seq[0]) || !read_char(seq[1]))
|
||||
{
|
||||
lock->lock();
|
||||
return -2;
|
||||
}
|
||||
lock->lock();
|
||||
if(seq[0] == '[')
|
||||
{
|
||||
if (seq[1] == 'D')
|
||||
{
|
||||
left_arrow:
|
||||
if (raw_cursor > 0)
|
||||
{
|
||||
raw_cursor--;
|
||||
prompt_refresh();
|
||||
}
|
||||
}
|
||||
else if ( seq[1] == 'C')
|
||||
{
|
||||
right_arrow:
|
||||
/* right arrow */
|
||||
if (size_t(raw_cursor) != raw_buffer.size())
|
||||
{
|
||||
raw_cursor++;
|
||||
prompt_refresh();
|
||||
}
|
||||
}
|
||||
else if (seq[1] == 'A' || seq[1] == 'B')
|
||||
{
|
||||
/* up and down arrow: history */
|
||||
if (history.size() > 1)
|
||||
{
|
||||
/* Update the current history entry before to
|
||||
* overwrite it with tne next one. */
|
||||
history[history_index] = raw_buffer;
|
||||
/* Show the new entry */
|
||||
history_index += (seq[1] == 'A') ? 1 : -1;
|
||||
if (history_index < 0)
|
||||
{
|
||||
history_index = 0;
|
||||
break;
|
||||
}
|
||||
else if (size_t(history_index) >= history.size())
|
||||
{
|
||||
history_index = history.size()-1;
|
||||
break;
|
||||
}
|
||||
raw_buffer = history[history_index];
|
||||
raw_cursor = raw_buffer.size();
|
||||
prompt_refresh();
|
||||
}
|
||||
}
|
||||
else if(seq[1] == 'H')
|
||||
{
|
||||
// home
|
||||
raw_cursor = 0;
|
||||
prompt_refresh();
|
||||
}
|
||||
else if(seq[1] == 'F')
|
||||
{
|
||||
// end
|
||||
raw_cursor = raw_buffer.size();
|
||||
prompt_refresh();
|
||||
}
|
||||
else if (seq[1] > '0' && seq[1] < '7')
|
||||
{
|
||||
// extended escape
|
||||
lock->unlock();
|
||||
if(!read_char(seq2))
|
||||
{
|
||||
lock->lock();
|
||||
return -2;
|
||||
}
|
||||
lock->lock();
|
||||
if (seq[1] == '3' && seq2 == '~' )
|
||||
{
|
||||
// delete
|
||||
if (raw_buffer.size() > 0 && size_t(raw_cursor) < raw_buffer.size())
|
||||
{
|
||||
raw_buffer.erase(raw_cursor,1);
|
||||
prompt_refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (raw_buffer.size() == size_t(raw_cursor))
|
||||
{
|
||||
raw_buffer.append(1,c);
|
||||
raw_cursor++;
|
||||
if (plen+raw_buffer.size() < size_t(get_columns()))
|
||||
{
|
||||
/* Avoid a full update of the line in the
|
||||
* trivial case. */
|
||||
if (::write(fd,&c,1) == -1) return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
prompt_refresh();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
raw_buffer.insert(raw_cursor,1,c);
|
||||
raw_cursor++;
|
||||
prompt_refresh();
|
||||
}
|
||||
break;
|
||||
case 21: // Ctrl+u, delete the whole line.
|
||||
raw_buffer.clear();
|
||||
raw_cursor = 0;
|
||||
prompt_refresh();
|
||||
break;
|
||||
case 11: // Ctrl+k, delete from current to end of line.
|
||||
raw_buffer.erase(raw_cursor);
|
||||
prompt_refresh();
|
||||
break;
|
||||
case 1: // Ctrl+a, go to the start of the line
|
||||
raw_cursor = 0;
|
||||
prompt_refresh();
|
||||
break;
|
||||
case 5: // ctrl+e, go to the end of the line
|
||||
raw_cursor = raw_buffer.size();
|
||||
prompt_refresh();
|
||||
break;
|
||||
case 12: // ctrl+l, clear screen
|
||||
clear();
|
||||
prompt_refresh();
|
||||
}
|
||||
}
|
||||
return raw_buffer.size();
|
||||
}
|
||||
FILE * dfout_C;
|
||||
bool supported_terminal;
|
||||
// state variables
|
||||
bool rawmode; // is raw mode active?
|
||||
termios orig_termios; // saved/restored by raw mode
|
||||
// current state
|
||||
enum console_state
|
||||
{
|
||||
con_unclaimed,
|
||||
con_lineedit
|
||||
} state;
|
||||
bool in_batch;
|
||||
std::string prompt; // current prompt string
|
||||
std::string raw_buffer; // current raw mode buffer
|
||||
int raw_cursor; // cursor position in the buffer
|
||||
// thread exit mechanism
|
||||
int exit_pipe[2];
|
||||
fd_set descriptor_set;
|
||||
};
|
||||
}
|
||||
|
||||
Console::Console()
|
||||
{
|
||||
d = 0;
|
||||
inited = false;
|
||||
// we can't create the mutex at this time. the SDL functions aren't hooked yet.
|
||||
wlock = new recursive_mutex();
|
||||
}
|
||||
Console::~Console()
|
||||
{
|
||||
if(inited)
|
||||
shutdown();
|
||||
if(wlock)
|
||||
delete wlock;
|
||||
if(d)
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool Console::init(bool sharing)
|
||||
{
|
||||
if(sharing)
|
||||
{
|
||||
inited = false;
|
||||
return false;
|
||||
}
|
||||
freopen("stdout.log", "w", stdout);
|
||||
d = new Private();
|
||||
// make our own weird streams so our IO isn't redirected
|
||||
d->dfout_C = fopen("/dev/tty", "w");
|
||||
std::cin.tie(this);
|
||||
clear();
|
||||
d->supported_terminal = !isUnsupportedTerm() && isatty(STDIN_FILENO);
|
||||
// init the exit mechanism
|
||||
pipe(d->exit_pipe);
|
||||
FD_ZERO(&d->descriptor_set);
|
||||
FD_SET(STDIN_FILENO, &d->descriptor_set);
|
||||
FD_SET(d->exit_pipe[0], &d->descriptor_set);
|
||||
inited = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Console::shutdown(void)
|
||||
{
|
||||
if(!d)
|
||||
return true;
|
||||
lock_guard <recursive_mutex> g(*wlock);
|
||||
if(d->rawmode)
|
||||
d->disable_raw();
|
||||
d->print("\n");
|
||||
inited = false;
|
||||
// kill the thing
|
||||
close(d->exit_pipe[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Console::begin_batch()
|
||||
{
|
||||
//color_ostream::begin_batch();
|
||||
|
||||
wlock->lock();
|
||||
|
||||
if (inited)
|
||||
d->begin_batch();
|
||||
}
|
||||
|
||||
void Console::end_batch()
|
||||
{
|
||||
if (inited)
|
||||
d->end_batch();
|
||||
|
||||
wlock->unlock();
|
||||
}
|
||||
|
||||
void Console::flush_proxy()
|
||||
{
|
||||
lock_guard <recursive_mutex> g(*wlock);
|
||||
if (inited)
|
||||
d->flush();
|
||||
}
|
||||
|
||||
void Console::add_text(color_value color, const std::string &text)
|
||||
{
|
||||
lock_guard <recursive_mutex> g(*wlock);
|
||||
if (inited)
|
||||
d->print_text(color, text);
|
||||
}
|
||||
|
||||
int Console::get_columns(void)
|
||||
{
|
||||
lock_guard <recursive_mutex> g(*wlock);
|
||||
int ret = -1;
|
||||
if(inited)
|
||||
ret = d->get_columns();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Console::get_rows(void)
|
||||
{
|
||||
lock_guard <recursive_mutex> g(*wlock);
|
||||
int ret = -1;
|
||||
if(inited)
|
||||
ret = d->get_rows();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Console::clear()
|
||||
{
|
||||
lock_guard <recursive_mutex> g(*wlock);
|
||||
if(inited)
|
||||
d->clear();
|
||||
}
|
||||
|
||||
void Console::gotoxy(int x, int y)
|
||||
{
|
||||
lock_guard <recursive_mutex> g(*wlock);
|
||||
if(inited)
|
||||
d->gotoxy(x,y);
|
||||
}
|
||||
|
||||
void Console::cursor(bool enable)
|
||||
{
|
||||
lock_guard <recursive_mutex> g(*wlock);
|
||||
if(inited)
|
||||
d->cursor(enable);
|
||||
}
|
||||
|
||||
int Console::lineedit(const std::string & prompt, std::string & output, CommandHistory & ch)
|
||||
{
|
||||
lock_guard <recursive_mutex> g(*wlock);
|
||||
int ret = -2;
|
||||
if(inited)
|
||||
ret = d->lineedit(prompt,output,wlock,ch);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Console::msleep (unsigned int msec)
|
||||
{
|
||||
if (msec > 1000) sleep(msec/1000000);
|
||||
usleep((msec % 1000000) * 1000);
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
/*
|
||||
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 <stdio.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
/*typedef struct interpose_s
|
||||
{
|
||||
void *new_func;
|
||||
void *orig_func;
|
||||
} interpose_t;*/
|
||||
|
||||
#include "DFHack.h"
|
||||
#include "Core.h"
|
||||
#include "Hooks.h"
|
||||
#include <iostream>
|
||||
|
||||
/*static const interpose_t interposers[] __attribute__ ((section("__DATA, __interpose"))) =
|
||||
{
|
||||
{ (void *)DFH_SDL_Init, (void *)SDL_Init },
|
||||
{ (void *)DFH_SDL_PollEvent, (void *)SDL_PollEvent },
|
||||
{ (void *)DFH_SDL_Quit, (void *)SDL_Quit },
|
||||
{ (void *)DFH_SDL_NumJoysticks, (void *)SDL_NumJoysticks },
|
||||
|
||||
};*/
|
||||
|
||||
/*******************************************************************************
|
||||
* SDL part starts here *
|
||||
*******************************************************************************/
|
||||
// hook - called for each game tick (or more often)
|
||||
DFhackCExport int SDL_NumJoysticks(void)
|
||||
{
|
||||
DFHack::Core & c = DFHack::Core::getInstance();
|
||||
return c.Update();
|
||||
}
|
||||
|
||||
// hook - called at program exit
|
||||
static void (*_SDL_Quit)(void) = 0;
|
||||
DFhackCExport void SDL_Quit(void)
|
||||
{
|
||||
DFHack::Core & c = DFHack::Core::getInstance();
|
||||
c.Shutdown();
|
||||
/*if(_SDL_Quit)
|
||||
{
|
||||
_SDL_Quit();
|
||||
}*/
|
||||
|
||||
_SDL_Quit();
|
||||
}
|
||||
|
||||
// called by DF to check input events
|
||||
static int (*_SDL_PollEvent)(SDL_Event* event) = 0;
|
||||
DFhackCExport int SDL_PollEvent(SDL_Event* event)
|
||||
{
|
||||
pollevent_again:
|
||||
// if SDL returns 0 here, it means there are no more events. return 0
|
||||
int orig_return = _SDL_PollEvent(event);
|
||||
if(!orig_return)
|
||||
return 0;
|
||||
// otherwise we have an event to filter
|
||||
else if( event != 0 )
|
||||
{
|
||||
DFHack::Core & c = DFHack::Core::getInstance();
|
||||
// if we consume the event, ask SDL for more.
|
||||
if(!c.DFH_SDL_Event(event))
|
||||
goto pollevent_again;
|
||||
}
|
||||
return orig_return;
|
||||
}
|
||||
|
||||
struct WINDOW;
|
||||
DFhackCExport int wgetch(WINDOW *win)
|
||||
{
|
||||
static int (*_wgetch)(WINDOW * win) = (int (*)( WINDOW * )) dlsym(RTLD_NEXT, "wgetch");
|
||||
if(!_wgetch)
|
||||
{
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
DFHack::Core & c = DFHack::Core::getInstance();
|
||||
wgetch_again:
|
||||
int in = _wgetch(win);
|
||||
int out;
|
||||
if(c.ncurses_wgetch(in, out))
|
||||
{
|
||||
// not consumed, give to DF
|
||||
return out;
|
||||
}
|
||||
else
|
||||
{
|
||||
// consumed, repeat
|
||||
goto wgetch_again;
|
||||
}
|
||||
}
|
||||
|
||||
// hook - called at program start, initialize some stuffs we'll use later
|
||||
static int (*_SDL_Init)(uint32_t flags) = 0;
|
||||
DFhackCExport int SDL_Init(uint32_t flags)
|
||||
{
|
||||
// reroute stderr
|
||||
fprintf(stderr,"dfhack: attempting to hook in\n");
|
||||
freopen("stderr.log", "w", stderr);
|
||||
// we don't reroute stdout until we figure out if this should be done at all
|
||||
// See: Console-linux.cpp
|
||||
|
||||
// find real functions
|
||||
fprintf(stderr,"dfhack: saving real SDL functions\n");
|
||||
_SDL_Init = (int (*)( uint32_t )) dlsym(RTLD_NEXT, "SDL_Init");
|
||||
_SDL_Quit = (void (*)( void )) dlsym(RTLD_NEXT, "SDL_Quit");
|
||||
_SDL_PollEvent = (int (*)(SDL_Event*))dlsym(RTLD_NEXT,"SDL_PollEvent");
|
||||
|
||||
fprintf(stderr,"dfhack: saved real SDL functions\n");
|
||||
// check if we got them
|
||||
if(_SDL_Init && _SDL_Quit && _SDL_PollEvent)
|
||||
{
|
||||
fprintf(stderr,"dfhack: hooking successful\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// bail, this would be a disaster otherwise
|
||||
fprintf(stderr,"dfhack: something went horribly wrong\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DFHack::Core & c = DFHack::Core::getInstance();
|
||||
//c.Init();
|
||||
|
||||
int ret = _SDL_Init(flags);
|
||||
return ret;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* MacPool.h
|
||||
* Handles creation and destruction of autorelease pool for DFHack on the Mac
|
||||
*/
|
||||
|
||||
#ifndef MACPOOL_H
|
||||
#define MACPOOL_H
|
||||
|
||||
int create_pool();
|
||||
int destroy_pool();
|
||||
|
||||
#endif
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* MacPool.m
|
||||
*
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MacPool.h"
|
||||
|
||||
NSAutoreleasePool *thePool;
|
||||
|
||||
int create_pool() {
|
||||
fprintf(stderr,"Creating autorelease pool\n");
|
||||
thePool = [[NSAutoreleasePool alloc] init];
|
||||
return 1;
|
||||
}
|
||||
|
||||
int destroy_pool() {
|
||||
fprintf(stderr,"Draining and releasing autorelease pool\n");
|
||||
[thePool drain];
|
||||
[thePool release];
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
#include <stdio.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "DFHack.h"
|
||||
#include "PluginManager.h"
|
||||
#include "Hooks.h"
|
||||
#include <iostream>
|
||||
|
||||
/*
|
||||
* Plugin loading functions
|
||||
*/
|
||||
namespace DFHack
|
||||
{
|
||||
DFLibrary * OpenPlugin (const char * filename)
|
||||
{
|
||||
dlerror();
|
||||
DFLibrary * ret = (DFLibrary *) dlopen(filename, RTLD_NOW);
|
||||
if(!ret)
|
||||
{
|
||||
std::cerr << dlerror() << std::endl;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void * LookupPlugin (DFLibrary * plugin ,const char * function)
|
||||
{
|
||||
return (DFLibrary *) dlsym((void *)plugin, function);
|
||||
}
|
||||
void ClosePlugin (DFLibrary * plugin)
|
||||
{
|
||||
dlclose((void *) plugin);
|
||||
}
|
||||
}
|
@ -0,0 +1,229 @@
|
||||
/*
|
||||
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 "Internal.h"
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <mach-o/dyld.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
using namespace std;
|
||||
|
||||
#include <md5wrapper.h>
|
||||
#include "MemAccess.h"
|
||||
#include "VersionInfoFactory.h"
|
||||
#include "VersionInfo.h"
|
||||
#include "Error.h"
|
||||
#include <string.h>
|
||||
using namespace DFHack;
|
||||
|
||||
Process::Process(VersionInfoFactory * known_versions)
|
||||
{
|
||||
int target_result;
|
||||
|
||||
char path[1024];
|
||||
char *real_path;
|
||||
uint32_t size = sizeof(path);
|
||||
if (_NSGetExecutablePath(path, &size) == 0) {
|
||||
real_path = realpath(path, NULL);
|
||||
}
|
||||
|
||||
identified = false;
|
||||
my_descriptor = 0;
|
||||
|
||||
md5wrapper md5;
|
||||
uint32_t length;
|
||||
uint8_t first_kb [1024];
|
||||
memset(first_kb, 0, sizeof(first_kb));
|
||||
// get hash of the running DF process
|
||||
string hash = md5.getHashFromFile(real_path, length, (char *) first_kb);
|
||||
// create linux process, add it to the vector
|
||||
VersionInfo * vinfo = known_versions->getVersionInfoByMD5(hash);
|
||||
if(vinfo)
|
||||
{
|
||||
my_descriptor = new VersionInfo(*vinfo);
|
||||
identified = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
char * wd = getcwd(NULL, 0);
|
||||
cerr << "Unable to retrieve version information.\n";
|
||||
cerr << "File: " << real_path << endl;
|
||||
cerr << "MD5: " << hash << endl;
|
||||
cerr << "working dir: " << wd << endl;
|
||||
cerr << "length:" << length << endl;
|
||||
cerr << "1KB hexdump follows:" << endl;
|
||||
for(int i = 0; i < 64; i++)
|
||||
{
|
||||
fprintf(stderr, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
first_kb[i*16],
|
||||
first_kb[i*16+1],
|
||||
first_kb[i*16+2],
|
||||
first_kb[i*16+3],
|
||||
first_kb[i*16+4],
|
||||
first_kb[i*16+5],
|
||||
first_kb[i*16+6],
|
||||
first_kb[i*16+7],
|
||||
first_kb[i*16+8],
|
||||
first_kb[i*16+9],
|
||||
first_kb[i*16+10],
|
||||
first_kb[i*16+11],
|
||||
first_kb[i*16+12],
|
||||
first_kb[i*16+13],
|
||||
first_kb[i*16+14],
|
||||
first_kb[i*16+15]
|
||||
);
|
||||
}
|
||||
free(wd);
|
||||
}
|
||||
}
|
||||
|
||||
Process::~Process()
|
||||
{
|
||||
// destroy our copy of the memory descriptor
|
||||
delete my_descriptor;
|
||||
}
|
||||
|
||||
string Process::doReadClassName (void * vptr)
|
||||
{
|
||||
//FIXME: BAD!!!!!
|
||||
char * typeinfo = Process::readPtr(((char *)vptr - 0x4));
|
||||
char * typestring = Process::readPtr(typeinfo + 0x4);
|
||||
string raw = readCString(typestring);
|
||||
size_t start = raw.find_first_of("abcdefghijklmnopqrstuvwxyz");// trim numbers
|
||||
size_t end = raw.length();
|
||||
return raw.substr(start,end-start);
|
||||
}
|
||||
|
||||
//FIXME: cross-reference with ELF segment entries?
|
||||
void Process::getMemRanges( vector<t_memrange> & ranges )
|
||||
{
|
||||
char buffer[1024];
|
||||
char permissions[5]; // r/-, w/-, x/-, p/s, 0
|
||||
|
||||
FILE *mapFile = ::fopen("/proc/self/maps", "r");
|
||||
size_t start, end, offset, device1, device2, node;
|
||||
|
||||
while (fgets(buffer, 1024, mapFile))
|
||||
{
|
||||
t_memrange temp;
|
||||
temp.name[0] = 0;
|
||||
sscanf(buffer, "%zx-%zx %s %zx %2zx:%2zx %zu %[^\n]",
|
||||
&start,
|
||||
&end,
|
||||
(char*)&permissions,
|
||||
&offset, &device1, &device2, &node,
|
||||
(char*)temp.name);
|
||||
temp.start = (void *) start;
|
||||
temp.end = (void *) end;
|
||||
temp.read = permissions[0] == 'r';
|
||||
temp.write = permissions[1] == 'w';
|
||||
temp.execute = permissions[2] == 'x';
|
||||
temp.shared = permissions[3] == 's';
|
||||
temp.valid = true;
|
||||
ranges.push_back(temp);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Process::getBase()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getdir (string dir, vector<string> &files)
|
||||
{
|
||||
DIR *dp;
|
||||
struct dirent *dirp;
|
||||
if((dp = opendir(dir.c_str())) == NULL)
|
||||
{
|
||||
cout << "Error(" << errno << ") opening " << dir << endl;
|
||||
return errno;
|
||||
}
|
||||
while ((dirp = readdir(dp)) != NULL) {
|
||||
files.push_back(string(dirp->d_name));
|
||||
}
|
||||
closedir(dp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Process::getThreadIDs(vector<uint32_t> & threads )
|
||||
{
|
||||
stringstream ss;
|
||||
vector<string> subdirs;
|
||||
if(getdir("/proc/self/task/",subdirs) != 0)
|
||||
{
|
||||
//FIXME: needs exceptions. this is a fatal error
|
||||
cerr << "unable to enumerate threads. This is BAD!" << endl;
|
||||
return false;
|
||||
}
|
||||
threads.clear();
|
||||
for(size_t i = 0; i < subdirs.size();i++)
|
||||
{
|
||||
uint32_t tid;
|
||||
if(sscanf(subdirs[i].c_str(),"%d", &tid))
|
||||
{
|
||||
threads.push_back(tid);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
string Process::getPath()
|
||||
{
|
||||
char path[1024];
|
||||
char *real_path;
|
||||
uint32_t size = sizeof(path);
|
||||
if (_NSGetExecutablePath(path, &size) == 0) {
|
||||
real_path = realpath(path, NULL);
|
||||
}
|
||||
std::string path_string(real_path);
|
||||
int last_slash = path_string.find_last_of("/");
|
||||
std::string directory = path_string.substr(0,last_slash);
|
||||
return directory;
|
||||
}
|
||||
|
||||
int Process::getPID()
|
||||
{
|
||||
return getpid();
|
||||
}
|
||||
|
||||
bool Process::setPermisions(const t_memrange & range,const t_memrange &trgrange)
|
||||
{
|
||||
int result;
|
||||
int protect=0;
|
||||
if(trgrange.read)protect|=PROT_READ;
|
||||
if(trgrange.write)protect|=PROT_WRITE;
|
||||
if(trgrange.execute)protect|=PROT_EXEC;
|
||||
result=mprotect((void *)range.start, (size_t)range.end-(size_t)range.start,protect);
|
||||
|
||||
return result==0;
|
||||
}
|
@ -1,210 +0,0 @@
|
||||
/*
|
||||
SDL - Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2009 Sam Lantinga
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Sam Lantinga
|
||||
slouken@libsdl.org
|
||||
*/
|
||||
|
||||
// Fake - only structs. Shamelessly pilfered from the SDL library.
|
||||
// Needed for processing its event types without polluting our namespaces with C garbage
|
||||
|
||||
#pragma once
|
||||
#include "SDL_keyboard.h"
|
||||
|
||||
namespace SDL
|
||||
{
|
||||
enum ButtonState
|
||||
{
|
||||
BTN_RELEASED = 0,
|
||||
BTN_PRESSED = 1
|
||||
};
|
||||
|
||||
/** Event enumerations */
|
||||
enum EventType
|
||||
{
|
||||
ET_NOEVENT = 0, /**< Unused (do not remove) */
|
||||
ET_ACTIVEEVENT, /**< Application loses/gains visibility */
|
||||
ET_KEYDOWN, /**< Keys pressed */
|
||||
ET_KEYUP, /**< Keys released */
|
||||
ET_MOUSEMOTION, /**< Mouse moved */
|
||||
ET_MOUSEBUTTONDOWN, /**< Mouse button pressed */
|
||||
ET_MOUSEBUTTONUP, /**< Mouse button released */
|
||||
ET_JOYAXISMOTION, /**< Joystick axis motion */
|
||||
ET_JOYBALLMOTION, /**< Joystick trackball motion */
|
||||
ET_JOYHATMOTION, /**< Joystick hat position change */
|
||||
ET_JOYBUTTONDOWN, /**< Joystick button pressed */
|
||||
ET_JOYBUTTONUP, /**< Joystick button released */
|
||||
ET_QUIT, /**< User-requested quit */
|
||||
ET_SYSWMEVENT, /**< System specific event */
|
||||
ET_EVENT_RESERVEDA, /**< Reserved for future use.. */
|
||||
ET_EVENT_RESERVEDB, /**< Reserved for future use.. */
|
||||
ET_VIDEORESIZE, /**< User resized video mode */
|
||||
ET_VIDEOEXPOSE, /**< Screen needs to be redrawn */
|
||||
ET_EVENT_RESERVED2, /**< Reserved for future use.. */
|
||||
ET_EVENT_RESERVED3, /**< Reserved for future use.. */
|
||||
ET_EVENT_RESERVED4, /**< Reserved for future use.. */
|
||||
ET_EVENT_RESERVED5, /**< Reserved for future use.. */
|
||||
ET_EVENT_RESERVED6, /**< Reserved for future use.. */
|
||||
ET_EVENT_RESERVED7, /**< Reserved for future use.. */
|
||||
/** Events ET_USEREVENT through ET_MAXEVENTS-1 are for your use */
|
||||
ET_USEREVENT = 24,
|
||||
/** This last event is only for bounding internal arrays
|
||||
* It is the number of bits in the event mask datatype -- Uint32
|
||||
*/
|
||||
ET_NUMEVENTS = 32
|
||||
};
|
||||
|
||||
/** Application visibility event structure */
|
||||
struct ActiveEvent
|
||||
{
|
||||
uint8_t type; /**< ET_ACTIVEEVENT */
|
||||
uint8_t gain; /**< Whether given states were gained or lost (1/0) */
|
||||
uint8_t state; /**< A mask of the focus states */
|
||||
};
|
||||
|
||||
/** Keyboard event structure */
|
||||
struct KeyboardEvent
|
||||
{
|
||||
uint8_t type; /**< ET_KEYDOWN or ET_KEYUP */
|
||||
uint8_t which; /**< The keyboard device index */
|
||||
uint8_t state; /**< BTN_PRESSED or BTN_RELEASED */
|
||||
keysym ksym;
|
||||
};
|
||||
|
||||
/** Mouse motion event structure */
|
||||
struct MouseMotionEvent
|
||||
{
|
||||
uint8_t type; /**< ET_MOUSEMOTION */
|
||||
uint8_t which; /**< The mouse device index */
|
||||
uint8_t state; /**< The current button state */
|
||||
uint16_t x, y; /**< The X/Y coordinates of the mouse */
|
||||
int16_t xrel; /**< The relative motion in the X direction */
|
||||
int16_t yrel; /**< The relative motion in the Y direction */
|
||||
};
|
||||
|
||||
/** Mouse button event structure */
|
||||
struct MouseButtonEvent
|
||||
{
|
||||
uint8_t type; /**< ET_MOUSEBUTTONDOWN or ET_MOUSEBUTTONUP */
|
||||
uint8_t which; /**< The mouse device index */
|
||||
uint8_t button; /**< The mouse button index */
|
||||
uint8_t state; /**< BTN_PRESSED or BTN_RELEASED */
|
||||
uint16_t x, y; /**< The X/Y coordinates of the mouse at press time */
|
||||
};
|
||||
|
||||
/** Joystick axis motion event structure */
|
||||
struct JoyAxisEvent
|
||||
{
|
||||
uint8_t type; /**< ET_JOYAXISMOTION */
|
||||
uint8_t which; /**< The joystick device index */
|
||||
uint8_t axis; /**< The joystick axis index */
|
||||
int16_t value; /**< The axis value (range: -32768 to 32767) */
|
||||
};
|
||||
|
||||
/** Joystick trackball motion event structure */
|
||||
struct JoyBallEvent
|
||||
{
|
||||
uint8_t type; /**< ET_JOYBALLMOTION */
|
||||
uint8_t which; /**< The joystick device index */
|
||||
uint8_t ball; /**< The joystick trackball index */
|
||||
int16_t xrel; /**< The relative motion in the X direction */
|
||||
int16_t yrel; /**< The relative motion in the Y direction */
|
||||
};
|
||||
|
||||
/** Joystick hat position change event structure */
|
||||
struct JoyHatEvent
|
||||
{
|
||||
uint8_t type; /**< ET_JOYHATMOTION */
|
||||
uint8_t which; /**< The joystick device index */
|
||||
uint8_t hat; /**< The joystick hat index */
|
||||
uint8_t value; /**< The hat position value:
|
||||
* SDL_HAT_LEFTUP SDL_HAT_UP SDL_HAT_RIGHTUP
|
||||
* SDL_HAT_LEFT SDL_HAT_CENTERED SDL_HAT_RIGHT
|
||||
* SDL_HAT_LEFTDOWN SDL_HAT_DOWN SDL_HAT_RIGHTDOWN
|
||||
* Note that zero means the POV is centered.
|
||||
*/
|
||||
};
|
||||
|
||||
/** Joystick button event structure */
|
||||
struct JoyButtonEvent
|
||||
{
|
||||
uint8_t type; /**< ET_JOYBUTTONDOWN or ET_JOYBUTTONUP */
|
||||
uint8_t which; /**< The joystick device index */
|
||||
uint8_t button; /**< The joystick button index */
|
||||
uint8_t state; /**< BTN_PRESSED or BTN_RELEASED */
|
||||
};
|
||||
|
||||
/** The "window resized" event
|
||||
* When you get this event, you are responsible for setting a new video
|
||||
* mode with the new width and height.
|
||||
*/
|
||||
struct ResizeEvent
|
||||
{
|
||||
uint8_t type; /**< ET_VIDEORESIZE */
|
||||
int w; /**< New width */
|
||||
int h; /**< New height */
|
||||
};
|
||||
|
||||
/** The "screen redraw" event */
|
||||
struct ExposeEvent
|
||||
{
|
||||
uint8_t type; /**< ET_VIDEOEXPOSE */
|
||||
};
|
||||
|
||||
/** The "quit requested" event */
|
||||
struct QuitEvent
|
||||
{
|
||||
uint8_t type; /**< ET_QUIT */
|
||||
};
|
||||
|
||||
/** A user-defined event type */
|
||||
struct UserEvent
|
||||
{
|
||||
uint8_t type; /**< ETL_USEREVENT through ET_NUMEVENTS-1 */
|
||||
int code; /**< User defined event code */
|
||||
void *data1; /**< User defined data pointer */
|
||||
void *data2; /**< User defined data pointer */
|
||||
};
|
||||
|
||||
/** If you want to use this event, you should include SDL_syswm.h */
|
||||
struct SysWMmsg;
|
||||
struct SysWMEvent
|
||||
{
|
||||
uint8_t type;
|
||||
SysWMmsg *msg;
|
||||
};
|
||||
|
||||
/** General event structure */
|
||||
union Event
|
||||
{
|
||||
uint8_t type;
|
||||
ActiveEvent active;
|
||||
KeyboardEvent key;
|
||||
MouseMotionEvent motion;
|
||||
MouseButtonEvent button;
|
||||
JoyAxisEvent jaxis;
|
||||
JoyBallEvent jball;
|
||||
JoyHatEvent jhat;
|
||||
JoyButtonEvent jbutton;
|
||||
ResizeEvent resize;
|
||||
ExposeEvent expose;
|
||||
QuitEvent quit;
|
||||
UserEvent user;
|
||||
SysWMEvent syswm;
|
||||
};
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
SDL - Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2009 Sam Lantinga
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Sam Lantinga
|
||||
slouken@libsdl.org
|
||||
*/
|
||||
|
||||
// Fake - only structs. Shamelessly pilfered from the SDL library.
|
||||
// Needed for processing its event types without polluting our namespaces with C garbage
|
||||
|
||||
#pragma once
|
||||
#include "SDL_keysym.h"
|
||||
#include <stdint.h>
|
||||
|
||||
namespace SDL
|
||||
{
|
||||
/** Keysym structure
|
||||
*
|
||||
* - The scancode is hardware dependent, and should not be used by general
|
||||
* applications. If no hardware scancode is available, it will be 0.
|
||||
*
|
||||
* - The 'unicode' translated character is only available when character
|
||||
* translation is enabled by the SDL_EnableUNICODE() API. If non-zero,
|
||||
* this is a UNICODE character corresponding to the keypress. If the
|
||||
* high 9 bits of the character are 0, then this maps to the equivalent
|
||||
* ASCII character:
|
||||
* @code
|
||||
* char ch;
|
||||
* if ( (keysym.unicode & 0xFF80) == 0 ) {
|
||||
* ch = keysym.unicode & 0x7F;
|
||||
* } else {
|
||||
* An international character..
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
typedef struct keysym
|
||||
{
|
||||
uint8_t scancode; /**< hardware specific scancode */
|
||||
Key sym; /**< SDL virtual keysym */
|
||||
Mod mod; /**< current key modifiers */
|
||||
uint16_t unicode; /**< translated character */
|
||||
} keysym;
|
||||
|
||||
/** This is the mask which refers to all hotkey bindings */
|
||||
#define ALL_HOTKEYS 0xFFFFFFFF
|
||||
}
|
@ -1,329 +0,0 @@
|
||||
/*
|
||||
SDL - Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2009 Sam Lantinga
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Sam Lantinga
|
||||
slouken@libsdl.org
|
||||
*/
|
||||
|
||||
// Fake - only structs. Shamelessly pilfered from the SDL library.
|
||||
// Needed for processing its event types without polluting our namespaces with C garbage
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace SDL
|
||||
{
|
||||
/** What we really want is a mapping of every raw key on the keyboard.
|
||||
* To support international keyboards, we use the range 0xA1 - 0xFF
|
||||
* as international virtual keycodes. We'll follow in the footsteps of X11...
|
||||
* @brief The names of the keys
|
||||
*/
|
||||
enum Key
|
||||
{
|
||||
/** @name ASCII mapped keysyms
|
||||
* The keyboard syms have been cleverly chosen to map to ASCII
|
||||
*/
|
||||
/*@{*/
|
||||
K_UNKNOWN = 0,
|
||||
K_FIRST = 0,
|
||||
K_BACKSPACE = 8,
|
||||
K_TAB = 9,
|
||||
K_CLEAR = 12,
|
||||
K_RETURN = 13,
|
||||
K_PAUSE = 19,
|
||||
K_ESCAPE = 27,
|
||||
K_SPACE = 32,
|
||||
K_EXCLAIM = 33,
|
||||
K_QUOTEDBL = 34,
|
||||
K_HASH = 35,
|
||||
K_DOLLAR = 36,
|
||||
K_AMPERSAND = 38,
|
||||
K_QUOTE = 39,
|
||||
K_LEFTPAREN = 40,
|
||||
K_RIGHTPAREN = 41,
|
||||
K_ASTERISK = 42,
|
||||
K_PLUS = 43,
|
||||
K_COMMA = 44,
|
||||
K_MINUS = 45,
|
||||
K_PERIOD = 46,
|
||||
K_SLASH = 47,
|
||||
K_0 = 48,
|
||||
K_1 = 49,
|
||||
K_2 = 50,
|
||||
K_3 = 51,
|
||||
K_4 = 52,
|
||||
K_5 = 53,
|
||||
K_6 = 54,
|
||||
K_7 = 55,
|
||||
K_8 = 56,
|
||||
K_9 = 57,
|
||||
K_COLON = 58,
|
||||
K_SEMICOLON = 59,
|
||||
K_LESS = 60,
|
||||
K_EQUALS = 61,
|
||||
K_GREATER = 62,
|
||||
K_QUESTION = 63,
|
||||
K_AT = 64,
|
||||
/*
|
||||
Skip uppercase letters
|
||||
*/
|
||||
K_LEFTBRACKET = 91,
|
||||
K_BACKSLASH = 92,
|
||||
K_RIGHTBRACKET = 93,
|
||||
K_CARET = 94,
|
||||
K_UNDERSCORE = 95,
|
||||
K_BACKQUOTE = 96,
|
||||
K_a = 97,
|
||||
K_b = 98,
|
||||
K_c = 99,
|
||||
K_d = 100,
|
||||
K_e = 101,
|
||||
K_f = 102,
|
||||
K_g = 103,
|
||||
K_h = 104,
|
||||
K_i = 105,
|
||||
K_j = 106,
|
||||
K_k = 107,
|
||||
K_l = 108,
|
||||
K_m = 109,
|
||||
K_n = 110,
|
||||
K_o = 111,
|
||||
K_p = 112,
|
||||
K_q = 113,
|
||||
K_r = 114,
|
||||
K_s = 115,
|
||||
K_t = 116,
|
||||
K_u = 117,
|
||||
K_v = 118,
|
||||
K_w = 119,
|
||||
K_x = 120,
|
||||
K_y = 121,
|
||||
K_z = 122,
|
||||
K_DELETE = 127,
|
||||
/* End of ASCII mapped keysyms */
|
||||
/*@}*/
|
||||
|
||||
/** @name International keyboard syms */
|
||||
/*@{*/
|
||||
K_WORLD_0 = 160, /* 0xA0 */
|
||||
K_WORLD_1 = 161,
|
||||
K_WORLD_2 = 162,
|
||||
K_WORLD_3 = 163,
|
||||
K_WORLD_4 = 164,
|
||||
K_WORLD_5 = 165,
|
||||
K_WORLD_6 = 166,
|
||||
K_WORLD_7 = 167,
|
||||
K_WORLD_8 = 168,
|
||||
K_WORLD_9 = 169,
|
||||
K_WORLD_10 = 170,
|
||||
K_WORLD_11 = 171,
|
||||
K_WORLD_12 = 172,
|
||||
K_WORLD_13 = 173,
|
||||
K_WORLD_14 = 174,
|
||||
K_WORLD_15 = 175,
|
||||
K_WORLD_16 = 176,
|
||||
K_WORLD_17 = 177,
|
||||
K_WORLD_18 = 178,
|
||||
K_WORLD_19 = 179,
|
||||
K_WORLD_20 = 180,
|
||||
K_WORLD_21 = 181,
|
||||
K_WORLD_22 = 182,
|
||||
K_WORLD_23 = 183,
|
||||
K_WORLD_24 = 184,
|
||||
K_WORLD_25 = 185,
|
||||
K_WORLD_26 = 186,
|
||||
K_WORLD_27 = 187,
|
||||
K_WORLD_28 = 188,
|
||||
K_WORLD_29 = 189,
|
||||
K_WORLD_30 = 190,
|
||||
K_WORLD_31 = 191,
|
||||
K_WORLD_32 = 192,
|
||||
K_WORLD_33 = 193,
|
||||
K_WORLD_34 = 194,
|
||||
K_WORLD_35 = 195,
|
||||
K_WORLD_36 = 196,
|
||||
K_WORLD_37 = 197,
|
||||
K_WORLD_38 = 198,
|
||||
K_WORLD_39 = 199,
|
||||
K_WORLD_40 = 200,
|
||||
K_WORLD_41 = 201,
|
||||
K_WORLD_42 = 202,
|
||||
K_WORLD_43 = 203,
|
||||
K_WORLD_44 = 204,
|
||||
K_WORLD_45 = 205,
|
||||
K_WORLD_46 = 206,
|
||||
K_WORLD_47 = 207,
|
||||
K_WORLD_48 = 208,
|
||||
K_WORLD_49 = 209,
|
||||
K_WORLD_50 = 210,
|
||||
K_WORLD_51 = 211,
|
||||
K_WORLD_52 = 212,
|
||||
K_WORLD_53 = 213,
|
||||
K_WORLD_54 = 214,
|
||||
K_WORLD_55 = 215,
|
||||
K_WORLD_56 = 216,
|
||||
K_WORLD_57 = 217,
|
||||
K_WORLD_58 = 218,
|
||||
K_WORLD_59 = 219,
|
||||
K_WORLD_60 = 220,
|
||||
K_WORLD_61 = 221,
|
||||
K_WORLD_62 = 222,
|
||||
K_WORLD_63 = 223,
|
||||
K_WORLD_64 = 224,
|
||||
K_WORLD_65 = 225,
|
||||
K_WORLD_66 = 226,
|
||||
K_WORLD_67 = 227,
|
||||
K_WORLD_68 = 228,
|
||||
K_WORLD_69 = 229,
|
||||
K_WORLD_70 = 230,
|
||||
K_WORLD_71 = 231,
|
||||
K_WORLD_72 = 232,
|
||||
K_WORLD_73 = 233,
|
||||
K_WORLD_74 = 234,
|
||||
K_WORLD_75 = 235,
|
||||
K_WORLD_76 = 236,
|
||||
K_WORLD_77 = 237,
|
||||
K_WORLD_78 = 238,
|
||||
K_WORLD_79 = 239,
|
||||
K_WORLD_80 = 240,
|
||||
K_WORLD_81 = 241,
|
||||
K_WORLD_82 = 242,
|
||||
K_WORLD_83 = 243,
|
||||
K_WORLD_84 = 244,
|
||||
K_WORLD_85 = 245,
|
||||
K_WORLD_86 = 246,
|
||||
K_WORLD_87 = 247,
|
||||
K_WORLD_88 = 248,
|
||||
K_WORLD_89 = 249,
|
||||
K_WORLD_90 = 250,
|
||||
K_WORLD_91 = 251,
|
||||
K_WORLD_92 = 252,
|
||||
K_WORLD_93 = 253,
|
||||
K_WORLD_94 = 254,
|
||||
K_WORLD_95 = 255, /* 0xFF */
|
||||
/*@}*/
|
||||
|
||||
/** @name Numeric keypad */
|
||||
/*@{*/
|
||||
K_KP0 = 256,
|
||||
K_KP1 = 257,
|
||||
K_KP2 = 258,
|
||||
K_KP3 = 259,
|
||||
K_KP4 = 260,
|
||||
K_KP5 = 261,
|
||||
K_KP6 = 262,
|
||||
K_KP7 = 263,
|
||||
K_KP8 = 264,
|
||||
K_KP9 = 265,
|
||||
K_KP_PERIOD = 266,
|
||||
K_KP_DIVIDE = 267,
|
||||
K_KP_MULTIPLY = 268,
|
||||
K_KP_MINUS = 269,
|
||||
K_KP_PLUS = 270,
|
||||
K_KP_ENTER = 271,
|
||||
K_KP_EQUALS = 272,
|
||||
/*@}*/
|
||||
|
||||
/** @name Arrows + Home/End pad */
|
||||
/*@{*/
|
||||
K_UP = 273,
|
||||
K_DOWN = 274,
|
||||
K_RIGHT = 275,
|
||||
K_LEFT = 276,
|
||||
K_INSERT = 277,
|
||||
K_HOME = 278,
|
||||
K_END = 279,
|
||||
K_PAGEUP = 280,
|
||||
K_PAGEDOWN = 281,
|
||||
/*@}*/
|
||||
|
||||
/** @name Function keys */
|
||||
/*@{*/
|
||||
K_F1 = 282,
|
||||
K_F2 = 283,
|
||||
K_F3 = 284,
|
||||
K_F4 = 285,
|
||||
K_F5 = 286,
|
||||
K_F6 = 287,
|
||||
K_F7 = 288,
|
||||
K_F8 = 289,
|
||||
K_F9 = 290,
|
||||
K_F10 = 291,
|
||||
K_F11 = 292,
|
||||
K_F12 = 293,
|
||||
K_F13 = 294,
|
||||
K_F14 = 295,
|
||||
K_F15 = 296,
|
||||
/*@}*/
|
||||
|
||||
/** @name Key state modifier keys */
|
||||
/*@{*/
|
||||
K_NUMLOCK = 300,
|
||||
K_CAPSLOCK = 301,
|
||||
K_SCROLLOCK = 302,
|
||||
K_RSHIFT = 303,
|
||||
K_LSHIFT = 304,
|
||||
K_RCTRL = 305,
|
||||
K_LCTRL = 306,
|
||||
K_RALT = 307,
|
||||
K_LALT = 308,
|
||||
K_RMETA = 309,
|
||||
K_LMETA = 310,
|
||||
K_LSUPER = 311, /**< Left "Windows" key */
|
||||
K_RSUPER = 312, /**< Right "Windows" key */
|
||||
K_MODE = 313, /**< "Alt Gr" key */
|
||||
K_COMPOSE = 314, /**< Multi-key compose key */
|
||||
/*@}*/
|
||||
|
||||
/** @name Miscellaneous function keys */
|
||||
/*@{*/
|
||||
K_HELP = 315,
|
||||
K_PRINT = 316,
|
||||
K_SYSREQ = 317,
|
||||
K_BREAK = 318,
|
||||
K_MENU = 319,
|
||||
K_POWER = 320, /**< Power Macintosh power key */
|
||||
K_EURO = 321, /**< Some european keyboards */
|
||||
K_UNDO = 322, /**< Atari keyboard has Undo */
|
||||
/*@}*/
|
||||
|
||||
/* Add any other keys here */
|
||||
|
||||
K_LAST
|
||||
};
|
||||
|
||||
/** Enumeration of valid key mods (possibly OR'd together) */
|
||||
enum Mod {
|
||||
KMOD_NONE = 0x0000,
|
||||
KMOD_LSHIFT= 0x0001,
|
||||
KMOD_RSHIFT= 0x0002,
|
||||
KMOD_LCTRL = 0x0040,
|
||||
KMOD_RCTRL = 0x0080,
|
||||
KMOD_LALT = 0x0100,
|
||||
KMOD_RALT = 0x0200,
|
||||
KMOD_LMETA = 0x0400,
|
||||
KMOD_RMETA = 0x0800,
|
||||
KMOD_NUM = 0x1000,
|
||||
KMOD_CAPS = 0x2000,
|
||||
KMOD_MODE = 0x4000,
|
||||
KMOD_RESERVED = 0x8000,
|
||||
KMOD_CTRL = (KMOD_LCTRL|KMOD_RCTRL),
|
||||
KMOD_SHIFT = (KMOD_LSHIFT|KMOD_RSHIFT),
|
||||
KMOD_ALT = (KMOD_LALT|KMOD_RALT),
|
||||
KMOD_META = (KMOD_LMETA|KMOD_RMETA)
|
||||
};
|
||||
}
|
@ -1 +1 @@
|
||||
Subproject commit ff87e784a8e274fcc3d1f57e57746735eb7285c2
|
||||
Subproject commit f0e931933dd553ad1e9b2a9acde2cca59791ed9e
|
@ -1 +1 @@
|
||||
Subproject commit 37a823541538023b9f3d0d1e8039cf32851de68d
|
||||
Subproject commit 17b653665567a5f1df628217820f76bb0b9c70a5
|
Loading…
Reference in New Issue