code:
/*
Minetest
Copyright (C) 2015 est31 <MTest31@outlook.com>

This program 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 program 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 program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "config.h"
#if USE_CURSES
#include "version.h"
#include "terminal_chat_console.h"
#include "porting.h"
#include "settings.h"
#include "util/numeric.h"
#include "util/string.h"

TerminalChatConsole g_term_console;

// include this last to avoid any conflicts
// (likes to set macros to common names, conflicting various stuff)
#if CURSES_HAVE_NCURSESW_NCURSES_H
#include <ncursesw/ncurses.h>
#elif CURSES_HAVE_NCURSESW_CURSES_H
#include <ncursesw/curses.h>
#elif CURSES_HAVE_CURSES_H
#include <curses.h>
#elif CURSES_HAVE_NCURSES_H
#include <ncurses.h>
#elif CURSES_HAVE_NCURSES_NCURSES_H
#include <ncurses/ncurses.h>
#elif CURSES_HAVE_NCURSES_CURSES_H
#include <ncurses/curses.h>
#endif

// Some functions to make drawing etc position independent
static bool reformat_backend(ChatBackend *backend, int rows, int cols)
{
	if (rows < 2)
		return false;
	backend->reformat(cols, rows - 2);
	return true;
}

static void move_for_backend(int row, int col)
{
	move(row + 1, col);
}

void TerminalChatConsole::initOfCurses()
{
	initscr();
	cbreak(); //raw();
	noecho();
	keypad(stdscr, TRUE);
	nodelay(stdscr, TRUE);
	timeout(100);

	// To make esc not delay up to one second. According to the internet,
	// this is the value vim uses, too.
	set_escdelay(25);

	getmaxyx(stdscr, m_rows, m_cols);
	m_can_draw_text = reformat_backend(&m_chat_backend, m_rows, m_cols);
}

void TerminalChatConsole::deInitOfCurses()
{
	endwin();
}

void *TerminalChatConsole::run()
{
	BEGIN_DEBUG_EXCEPTION_HANDLER

	std::cout << "========================" << std::endl;
	std::cout << "Begin log output over terminal"
		<< " (no stdout/stderr backlog during that)" << std::endl;
	// Make the loggers to stdout/stderr shut up.
	// Go over our own loggers instead.
	LogLevelMask err_mask = g_logger.removeOutput(&stderr_output);
	LogLevelMask out_mask = g_logger.removeOutput(&stdout_output);

	g_logger.addOutput(&m_log_output);

	// Inform the server of our nick
	m_chat_interface->command_queue.push_back(
		new ChatEventNick(CET_NICK_ADD, m_nick));

	{
		// Ensures that curses is deinitialized even on an exception being thrown
		CursesInitHelper helper(this);

		while (!stopRequested()) {

			int ch = getch();
			if (stopRequested())
				break;

			step(ch);
		}
	}

	if (m_kill_requested)
		*m_kill_requested = true;

	g_logger.removeOutput(&m_log_output);
	g_logger.addOutputMasked(&stderr_output, err_mask);
	g_logger.addOutputMasked(&stdout_output, out_mask);

	std::cout << "End log output over terminal"
		<< " (no stdout/stderr backlog during that)" << std::endl;
	std::cout << "========================" << std::endl;

	END_DEBUG_EXCEPTION_HANDLER

	return NULL;
}

void TerminalChatConsole::typeChatMessage(const std::wstring &msg)
{
	// Discard empty line
	if (msg.empty())
		return;

	// Send to server
	m_chat_interface->command_queue.push_back(
		new ChatEventChat(m_nick, msg));

	// Print if its a command (gets eaten by server otherwise)
	if (msg[0] == L'/') {
		m_chat_backend.addMessage(L"", (std::wstring)L"Issued command: " + msg);
	}
}

void TerminalChatConsole::handleInput(int ch, bool &complete_redraw_needed)
{
	ChatPrompt &prompt = m_chat_backend.getPrompt();
	// Helpful if you want to collect key codes that aren't documented
	/*if (ch != ERR) {
		m_chat_backend.addMessage(L"",
			(std::wstring)L"Pressed key " + utf8_to_wide(
			std::string(keyname(ch)) + " (code " + itos(ch) + ")"));
		complete_redraw_needed = true;
	}//*/

	// All the key codes below are compatible to xterm
	// Only add new ones if you have tried them there,
	// to ensure compatibility with not just xterm but the wide
	// range of terminals that are compatible to xterm.

	switch (ch) {
		case ERR: // no input
			break;
		case 27: // ESC
			// Toggle ESC mode
			m_esc_mode = !m_esc_mode;
			break;
		case KEY_PPAGE:
			m_chat_backend.scrollPageUp();
			complete_redraw_needed = true;
			break;
		case KEY_NPAGE:
			m_chat_backend.scrollPageDown();
			complete_redraw_needed = true;
			break;
		case KEY_ENTER:
		case '\r':
		case '\n': {
			prompt.addToHistory(prompt.getLine());
			typeChatMessage(prompt.replace(L""));
			break;
		}
		case KEY_UP:
			prompt.historyPrev();
			break;
		case KEY_DOWN:
			prompt.historyNext();
			break;
		case KEY_LEFT:
			// Left pressed
			// move character to the left
			prompt.cursorOperation(
				ChatPrompt::CURSOROP_MOVE,
				ChatPrompt::CURSOROP_DIR_LEFT,
				ChatPrompt::CURSOROP_SCOPE_CHARACTER);
			break;
		case 545:
			// Ctrl-Left pressed
			// move word to the left
			prompt.cursorOperation(
				ChatPrompt::CURSOROP_MOVE,
				ChatPrompt::CURSOROP_DIR_LEFT,
				ChatPrompt::CURSOROP_SCOPE_WORD);
			break;
		case KEY_RIGHT:
			// Right pressed
			// move character to the right
			prompt.cursorOperation(
				ChatPrompt::CURSOROP_MOVE,
				ChatPrompt::CURSOROP_DIR_RIGHT,
				ChatPrompt::CURSOROP_SCOPE_CHARACTER);
			break;
		case 560:
			// Ctrl-Right pressed
			// move word to the right
			prompt.cursorOperation(
				ChatPrompt::CURSOROP_MOVE,
				ChatPrompt::CURSOROP_DIR_RIGHT,
				ChatPrompt::CURSOROP_SCOPE_WORD);
			break;
		case KEY_HOME:
			// Home pressed
			// move to beginning of line
			prompt.cursorOperation(
				ChatPrompt::CURSOROP_MOVE,
				ChatPrompt::CURSOROP_DIR_LEFT,
				ChatPrompt::CURSOROP_SCOPE_LINE);
			break;
		case KEY_END:
			// End pressed
			// move to end of line
			prompt.cursorOperation(
				ChatPrompt::CURSOROP_MOVE,
				ChatPrompt::CURSOROP_DIR_RIGHT,
				ChatPrompt::CURSOROP_SCOPE_LINE);
			break;
		case KEY_BACKSPACE:
		case '\b':
		case 127:
			// Backspace pressed
			// delete character to the left
			prompt.cursorOperation(
				ChatPrompt::CURSOROP_DELETE,
				ChatPrompt::CURSOROP_DIR_LEFT,
				ChatPrompt::CURSOROP_SCOPE_CHARACTER);
			break;
		case KEY_DC:
			// Delete pressed
			// delete character to the right
			prompt.cursorOperation(
				ChatPrompt::CURSOROP_DELETE,
				ChatPrompt::CURSOROP_DIR_RIGHT,
				ChatPrompt::CURSOROP_SCOPE_CHARACTER);
			break;
		case 519:
			// Ctrl-Delete pressed
			// delete word to the right
			prompt.cursorOperation(
				ChatPrompt::CURSOROP_DELETE,
				ChatPrompt::CURSOROP_DIR_RIGHT,
				ChatPrompt::CURSOROP_SCOPE_WORD);
			break;
		case 21:
			// Ctrl-U pressed
			// kill line to left end
			prompt.cursorOperation(
				ChatPrompt::CURSOROP_DELETE,
				ChatPrompt::CURSOROP_DIR_LEFT,
				ChatPrompt::CURSOROP_SCOPE_LINE);
			break;
		case 11:
			// Ctrl-K pressed
			// kill line to right end
			prompt.cursorOperation(
				ChatPrompt::CURSOROP_DELETE,
				ChatPrompt::CURSOROP_DIR_RIGHT,
				ChatPrompt::CURSOROP_SCOPE_LINE);
			break;
		case KEY_TAB:
			// Tab pressed
			// Nick completion
			prompt.nickCompletion(m_nicks, false);
			break;
		default:
			// Add character to the prompt,
			// assuming UTF-8.
			if (IS_UTF8_MULTB_START(ch)) {
				m_pending_utf8_bytes.append(1, (char)ch);
				m_utf8_bytes_to_wait += UTF8_MULTB_START_LEN(ch) - 1;
			} else if (m_utf8_bytes_to_wait != 0) {
				m_pending_utf8_bytes.append(1, (char)ch);
				m_utf8_bytes_to_wait--;
				if (m_utf8_bytes_to_wait == 0) {
					std::wstring w = utf8_to_wide(m_pending_utf8_bytes);
					m_pending_utf8_bytes = "";
					// hopefully only one char in the wstring...
					for (size_t i = 0; i < w.size(); i++) {
						prompt.input(w.c_str()[i]);
					}
				}
			} else if (IS_ASCII_PRINTABLE_CHAR(ch)) {
				prompt.input(ch);
			} else {
				// Silently ignore characters we don't handle

				//warningstream << "Pressed invalid character '"
				//	<< keyname(ch) << "' (code " << itos(ch) << ")" << std::endl;
			}
			break;
	}
}

void TerminalChatConsole::step(int ch)
{
	bool complete_redraw_needed = false;

	// empty queues
	while (!m_chat_interface->outgoing_queue.empty()) {
		ChatEvent *evt = m_chat_interface->outgoing_queue.pop_frontNoEx();
		switch (evt->type) {
			case CET_NICK_REMOVE:
				m_nicks.remove(((ChatEventNick *)evt)->nick);
				break;
			case CET_NICK_ADD:
				m_nicks.push_back(((ChatEventNick *)evt)->nick);
				break;
			case CET_CHAT:
				complete_redraw_needed = true;
				// This is only used for direct replies from commands
				// or for lua's print() functionality
				m_chat_backend.addMessage(L"", ((ChatEventChat *)evt)->evt_msg);
				break;
			case CET_TIME_INFO:
				ChatEventTimeInfo *tevt = (ChatEventTimeInfo *)evt;
				m_game_time = tevt->game_time;
				m_time_of_day = tevt->time;
		};
		delete evt;
	}
	while (!m_log_output.queue.empty()) {
		complete_redraw_needed = true;
		std::pair<LogLevel, std::string> p = m_log_output.queue.pop_frontNoEx();
		if (p.first > m_log_level)
			continue;

		std::wstring error_message = utf8_to_wide(Logger::getLevelLabel(p.first));
		if (!g_settings->getBool("disable_escape_sequences")) {
			error_message = L"\x1b(c@red)" + error_message + L"\x1b(c@white)";
		}
		m_chat_backend.addMessage(error_message, utf8_to_wide(p.second));
	}

	// handle input
	if (!m_esc_mode) {
		handleInput(ch, complete_redraw_needed);
	} else {
		switch (ch) {
			case ERR: // no input
				break;
			case 27: // ESC
				// Toggle ESC mode
				m_esc_mode = !m_esc_mode;
				break;
			case 'L':
				m_log_level--;
				m_log_level = MYMAX(m_log_level, LL_NONE + 1); // LL_NONE isn't accessible
				break;
			case 'l':
				m_log_level++;
				m_log_level = MYMIN(m_log_level, LL_MAX - 1);
				break;
		}
	}

	// was there a resize?
	int xn, yn;
	getmaxyx(stdscr, yn, xn);
	if (xn != m_cols || yn != m_rows) {
		m_cols = xn;
		m_rows = yn;
		m_can_draw_text = reformat_backend(&m_chat_backend, m_rows, m_cols);
		complete_redraw_needed = true;
	}

	// draw title
	move(0, 0);
	clrtoeol();
	addstr(PROJECT_NAME_C);
	addstr(" ");
	addstr(g_version_hash);

	u32 minutes = m_time_of_day % 1000;
	u32 hours = m_time_of_day / 1000;
	minutes = (float)minutes / 1000 * 60;

	if (m_game_time)
		printw(" | Game %d Time of day %02d:%02d ",
			m_game_time, hours, minutes);

	// draw text
	if (complete_redraw_needed && m_can_draw_text)
		draw_text();

	// draw prompt
	if (!m_esc_mode) {
		// normal prompt
		ChatPrompt& prompt = m_chat_backend.getPrompt();
		std::string prompt_text = wide_to_utf8(prompt.getVisiblePortion());
		move(m_rows - 1, 0);
		clrtoeol();
		addstr(prompt_text.c_str());
		// Draw cursor
		s32 cursor_pos = prompt.getVisibleCursorPosition();
		if (cursor_pos >= 0) {
			move(m_rows - 1, cursor_pos);
		}
	} else {
		// esc prompt
		move(m_rows - 1, 0);
		clrtoeol();
		printw("[ESC] Toggle ESC mode |"
			" [CTRL+C] Shut down |"
			" (L) in-, (l) decrease loglevel %s",
			Logger::getLevelLabel((LogLevel) m_log_level).c_str());
	}

	refresh();
}

void TerminalChatConsole::draw_text()
{
	ChatBuffer& buf = m_chat_backend.getConsoleBuffer();
	for (u32 row = 0; row < buf.getRows(); row++) {
		move_for_backend(row, 0);
		clrtoeol();
		const ChatFormattedLine& line = buf.getFormattedLine(row);
		if (line.fragments.empty())
			continue;
		for (u32 i = 0; i < line.fragments.size(); ++i) {
			const ChatFormattedFragment& fragment = line.fragments[i];
			addstr(wide_to_utf8(fragment.text.getString()).c_str());
		}
	}
}

void TerminalChatConsole::stopAndWaitforThread()
{
	clearKillStatus();
	stop();
	wait();
}

#endif


code:
/*
Minetest
Copyright (C) 2015 est31 <MTest31@outlook.com>

This program 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 program 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 program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef TERMINAL_CHAT_CONSOLE_H
#define TERMINAL_CHAT_CONSOLE_H

#include "chat.h"
#include "threading/thread.h"
#include "chat_interface.h"
#include "log.h"

#include <sstream>

class TermLogOutput : public ILogOutput {
public:

	void logRaw(LogLevel lev, const std::string &line)
	{
		queue.push_back(std::make_pair(lev, line));
	}

	virtual void log(LogLevel lev, const std::string &combined,
		const std::string &time, const std::string &thread_name,
		const std::string &payload_text)
	{
		std::ostringstream os(std::ios_base::binary);
		os << time << ": [" << thread_name << "] " << payload_text;

		queue.push_back(std::make_pair(lev, os.str()));
	}

	MutexedQueue<std::pair<LogLevel, std::string> > queue;
};

class TerminalChatConsole : public Thread {
public:

	TerminalChatConsole() :
		Thread("TerminalThread"),
		m_log_level(LL_ACTION),
		m_utf8_bytes_to_wait(0),
		m_kill_requested(NULL),
		m_esc_mode(false),
		m_game_time(0),
		m_time_of_day(0)
	{}

	void setup(
		ChatInterface *iface,
		bool *kill_requested,
		const std::string &nick)
	{
		m_nick = nick;
		m_kill_requested = kill_requested;
		m_chat_interface = iface;
	}

	virtual void *run();

	// Highly required!
	void clearKillStatus() { m_kill_requested = NULL; }

	void stopAndWaitforThread();

private:
	// these have stupid names so that nobody missclassifies them
	// as curses functions. Oh, curses has stupid names too?
	// Well, at least it was worth a try...
	void initOfCurses();
	void deInitOfCurses();

	void draw_text();

	void typeChatMessage(const std::wstring &m);

	void handleInput(int ch, bool &complete_redraw_needed);

	void step(int ch);

	// Used to ensure the deinitialisation is always called.
	struct CursesInitHelper {
		TerminalChatConsole *cons;
		CursesInitHelper(TerminalChatConsole * a_console)
			: cons(a_console)
		{ cons->initOfCurses(); }
		~CursesInitHelper() { cons->deInitOfCurses(); }
	};

	int m_log_level;
	std::string m_nick;

	u8 m_utf8_bytes_to_wait;
	std::string m_pending_utf8_bytes;

	std::list<std::string> m_nicks;

	int m_cols;
	int m_rows;
	bool m_can_draw_text;

	bool *m_kill_requested;
	ChatBackend m_chat_backend;
	ChatInterface *m_chat_interface;

	TermLogOutput m_log_output;

	bool m_esc_mode;

	u64 m_game_time;
	u32 m_time_of_day;
};

extern TerminalChatConsole g_term_console;

#endif

repo_name: pgimeno/minetest

license: unknown


code:
set(JTHREAD_SRCS
	${CMAKE_CURRENT_SOURCE_DIR}/event.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/mutex.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/thread.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/semaphore.cpp
	PARENT_SCOPE)


code:
/*
Minetest
Copyright (C) 2015 ShadowNinja <shadowninja@minetest.net>

This program 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 program 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 program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef THREADING_ATOMIC_H
#define THREADING_ATOMIC_H

#if __cplusplus >= 201103L
	#include <atomic>
	template<typename T> using Atomic = std::atomic<T>;
	template<typename T> using GenericAtomic = std::atomic<T>;
#else

#define GCC_VERSION   (__GNUC__        * 100 + __GNUC_MINOR__)
#define CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
#if GCC_VERSION >= 407 || CLANG_VERSION >= 302
	#define ATOMIC_LOAD_GENERIC(T, v) do {               \
		T _val;                                          \
		 __atomic_load(&(v), &(_val), __ATOMIC_SEQ_CST); \
		return _val;                                     \
	} while(0)
	#define ATOMIC_LOAD(T, v)        return __atomic_load_n    (&(v),       __ATOMIC_SEQ_CST)
	#define ATOMIC_STORE(T, v, x)           __atomic_store     (&(v), &(x), __ATOMIC_SEQ_CST); return x
	#define ATOMIC_EXCHANGE(T, v, x) return __atomic_exchange  (&(v), &(x), __ATOMIC_SEQ_CST)
	#define ATOMIC_ADD_EQ(T, v, x)   return __atomic_add_fetch (&(v), (x),  __ATOMIC_SEQ_CST)
	#define ATOMIC_SUB_EQ(T, v, x)   return __atomic_sub_fetch (&(v), (x),  __ATOMIC_SEQ_CST)
	#define ATOMIC_POST_INC(T, v)    return __atomic_fetch_add (&(v), 1,    __ATOMIC_SEQ_CST)
	#define ATOMIC_POST_DEC(T, v)    return __atomic_fetch_sub (&(v), 1,    __ATOMIC_SEQ_CST)
	#define ATOMIC_CAS(T, v, e, d)   return __atomic_compare_exchange(&(v), &(e), &(d), \
		false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
#else
	#define ATOMIC_USE_LOCK
	#include "threading/mutex.h"

	#define ATOMIC_LOCK_OP(T, op) do { \
			m_mutex.lock();            \
			T _val = (op);             \
			m_mutex.unlock();          \
			return _val;               \
		} while (0)
	#define ATOMIC_LOCK_CAS(T, v, e, d) do { \
			m_mutex.lock();                  \
			bool _eq = (v == e);             \
			if (_eq)                         \
				v = d;                       \
			m_mutex.unlock();                \
			return _eq;                      \
		} while (0)
	#define ATOMIC_LOAD(T, v) ATOMIC_LOCK_OP(T, v)
	#define ATOMIC_LOAD_GENERIC(T, v) ATOMIC_LOAD(T, v)
	#define ATOMIC_STORE(T, v, x) ATOMIC_LOCK_OP(T, v = x)
	#define ATOMIC_EXCHANGE(T, v, x) do { \
			m_mutex.lock();               \
			T _val = v;                   \
			v = x;                        \
			m_mutex.unlock();             \
			return _val;                  \
		} while (0)
	#if GCC_VERSION >= 401
		#define ATOMIC_ADD_EQ(T, v, x) return __sync_add_and_fetch(&(v), (x))
		#define ATOMIC_SUB_EQ(T, v, x) return __sync_sub_and_fetch(&(v), (x))
		#define ATOMIC_POST_INC(T, v)  return __sync_fetch_and_add(&(v), 1)
		#define ATOMIC_POST_DEC(T, v)  return __sync_fetch_and_sub(&(v), 1)
		#define ATOMIC_CAS(T, v, e, d) return __sync_bool_compare_and_swap(&(v), &(e), (d))
	#else
		#define ATOMIC_ADD_EQ(T, v, x) ATOMIC_LOCK_OP(T, v += x)
		#define ATOMIC_SUB_EQ(T, v, x) ATOMIC_LOCK_OP(T, v -= x)
		#define ATOMIC_POST_INC(T, v)  ATOMIC_LOCK_OP(T, v++)
		#define ATOMIC_POST_DEC(T, v)  ATOMIC_LOCK_OP(T, v--)
		#define ATOMIC_CAS(T, v, e, d) ATOMIC_LOCK_CAS(T, v, e, d)
	#endif
#endif

// For usage with integral types.
template<typename T>
class Atomic {
public:
	Atomic(const T &v = 0) : m_val(v) {}

	operator T () { ATOMIC_LOAD(T, m_val); }

	T exchange(T x)  { ATOMIC_EXCHANGE(T, m_val, x); }
	bool compare_exchange_strong(T &expected, T desired) { ATOMIC_CAS(T, m_val, expected, desired); }

	T operator =  (T x) { ATOMIC_STORE(T, m_val, x); }
	T operator += (T x) { ATOMIC_ADD_EQ(T, m_val, x); }
	T operator -= (T x) { ATOMIC_SUB_EQ(T, m_val, x); }
	T operator ++ ()    { return *this += 1; }
	T operator -- ()    { return *this -= 1; }
	T operator ++ (int) { ATOMIC_POST_INC(T, m_val); }
	T operator -- (int) { ATOMIC_POST_DEC(T, m_val); }
private:
	T m_val;
#ifdef ATOMIC_USE_LOCK
	Mutex m_mutex;
#endif
};

// For usage with non-integral types like float for example.
// Needed because the other operations aren't provided by gcc
// for non-integral types:
// https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/_005f_005fatomic-Builtins.html
template<typename T>
class GenericAtomic {
public:
	GenericAtomic(const T &v = 0) : m_val(v) {}

	operator T () { ATOMIC_LOAD_GENERIC(T, m_val); }

	T exchange(T x)  { ATOMIC_EXCHANGE(T, m_val, x); }
	bool compare_exchange_strong(T &expected, T desired) { ATOMIC_CAS(T, m_val, expected, desired); }

	T operator = (T x) { ATOMIC_STORE(T, m_val, x); }
private:
	T m_val;
#ifdef ATOMIC_USE_LOCK
	Mutex m_mutex;
#endif
};

#endif  // C++11

#endif


code:
/*
This file is a part of the JThread package, which contains some object-
oriented thread wrappers for different thread implementations.

Copyright (c) 2000-2006  Jori Liesenborgs (jori.liesenborgs@gmail.com)

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

#include "threading/event.h"

Event::Event()
{
#ifndef USE_CPP11_MUTEX
#	if USE_WIN_MUTEX
	event = CreateEvent(NULL, false, false, NULL);
#	else
	pthread_cond_init(&cv, NULL);
	pthread_mutex_init(&mutex, NULL);
	notified = false;
#	endif
#elif USE_CPP11_MUTEX
	notified = false;
#endif
}

#ifndef USE_CPP11_MUTEX
Event::~Event()
{
#if USE_WIN_MUTEX
	CloseHandle(event);
#else
	pthread_cond_destroy(&cv);
	pthread_mutex_destroy(&mutex);
#endif
}
#endif

void Event::wait()
{
#if USE_CPP11_MUTEX
	MutexAutoLock lock(mutex);
	while (!notified) {
		cv.wait(lock);
	}
	notified = false;
#elif USE_WIN_MUTEX
	WaitForSingleObject(event, INFINITE);
#else
	pthread_mutex_lock(&mutex);
	while (!notified) {
		pthread_cond_wait(&cv, &mutex);
	}
	notified = false;
	pthread_mutex_unlock(&mutex);
#endif
}

void Event::signal()
{
#if USE_CPP11_MUTEX
	MutexAutoLock lock(mutex);
	notified = true;
	cv.notify_one();
#elif USE_WIN_MUTEX
	SetEvent(event);
#else
	pthread_mutex_lock(&mutex);
	notified = true;
	pthread_cond_signal(&cv);
	pthread_mutex_unlock(&mutex);
#endif
}


code:
/*
This file is a part of the JThread package, which contains some object-
oriented thread wrappers for different thread implementations.

Copyright (c) 2000-2006  Jori Liesenborgs (jori.liesenborgs@gmail.com)

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

#ifndef THREADING_EVENT_H
#define THREADING_EVENT_H

#include "threads.h"

#if USE_CPP11_MUTEX
#include <condition_variable>
#include "threading/mutex.h"
#include "threading/mutex_auto_lock.h"
#endif

/** A syncronization primitive that will wake up one waiting thread when signaled.
 * Calling @c signal() multiple times before a waiting thread has had a chance
 * to notice the signal will wake only one thread.  Additionally, if no threads
 * are waiting on the event when it is signaled, the next call to @c wait()
 * will return (almost) immediately.
 */
class Event
{
public:
	Event();
#ifndef USE_CPP11_MUTEX
	~Event();
#endif
	void wait();
	void signal();

private:
#if USE_CPP11_MUTEX
	std::condition_variable cv;
	Mutex mutex;
	bool notified;
#elif USE_WIN_MUTEX
	HANDLE event;
#else
	pthread_cond_t cv;
	pthread_mutex_t mutex;
	bool notified;
#endif
};

#endif


code:
/*
This file is a part of the JThread package, which contains some object-
oriented thread wrappers for different thread implementations.

Copyright (c) 2000-2006  Jori Liesenborgs (jori.liesenborgs@gmail.com)

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

#include "threads.h"

#ifndef USE_CPP11_MUTEX

#include "threading/mutex.h"

#include <cassert>

#define UNUSED(expr) do { (void)(expr); } while (0)

Mutex::Mutex()
{
	init_mutex(false);
}

Mutex::Mutex(bool recursive)
{
	init_mutex(recursive);
}

void Mutex::init_mutex(bool recursive)
{
#if USE_WIN_MUTEX
	// Windows critical sections are recursive by default
	UNUSED(recursive);

	InitializeCriticalSection(&mutex);
#else
	pthread_mutexattr_t attr;
	pthread_mutexattr_init(&attr);

	if (recursive)
		pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);

	int ret = pthread_mutex_init(&mutex, &attr);
	assert(!ret);
	UNUSED(ret);

	pthread_mutexattr_destroy(&attr);
#endif
}

Mutex::~Mutex()
{
#if USE_WIN_MUTEX
	DeleteCriticalSection(&mutex);
#else
	int ret = pthread_mutex_destroy(&mutex);
	assert(!ret);
	UNUSED(ret);
#endif
}

void Mutex::lock()
{
#if USE_WIN_MUTEX
	EnterCriticalSection(&mutex);
#else
	int ret = pthread_mutex_lock(&mutex);
	assert(!ret);
	UNUSED(ret);
#endif
}

bool Mutex::try_lock()
{
#if USE_WIN_MUTEX
	return TryEnterCriticalSection(&mutex) != 0;
#else
	return pthread_mutex_trylock(&mutex) == 0;
#endif
}

void Mutex::unlock()
{
#if USE_WIN_MUTEX
	LeaveCriticalSection(&mutex);
#else
	int ret = pthread_mutex_unlock(&mutex);
	assert(!ret);
	UNUSED(ret);
#endif
}

RecursiveMutex::RecursiveMutex()
	: Mutex(true)
{}

#endif // C++11


code:
/*
This file is a part of the JThread package, which contains some object-
oriented thread wrappers for different thread implementations.

Copyright (c) 2000-2006  Jori Liesenborgs (jori.liesenborgs@gmail.com)

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

#ifndef THREADING_MUTEX_H
#define THREADING_MUTEX_H

#include "threads.h"

#if USE_CPP11_MUTEX
	#include <mutex>
	using Mutex = std::mutex;
	using RecursiveMutex = std::recursive_mutex;
#else

#if USE_WIN_MUTEX
	#ifndef _WIN32_WINNT
		#define _WIN32_WINNT 0x0501
	#endif
	#ifndef WIN32_LEAN_AND_MEAN
		#define WIN32_LEAN_AND_MEAN
	#endif
	#include <windows.h>
#else
	#include <pthread.h>
#endif

#include "util/basic_macros.h"

class Mutex
{
public:
	Mutex();
	~Mutex();
	void lock();
	void unlock();

	bool try_lock();

protected:
	Mutex(bool recursive);
	void init_mutex(bool recursive);
private:
#if USE_WIN_MUTEX
	CRITICAL_SECTION mutex;
#else
	pthread_mutex_t mutex;
#endif

	DISABLE_CLASS_COPY(Mutex);
};

class RecursiveMutex : public Mutex
{
public:
	RecursiveMutex();

	DISABLE_CLASS_COPY(RecursiveMutex);
};

#endif // C++11

#endif


code:
/*
This file is a part of the JThread package, which contains some object-
oriented thread wrappers for different thread implementations.

Copyright (c) 2000-2006  Jori Liesenborgs (jori.liesenborgs@gmail.com)

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

#ifndef THREADING_MUTEX_AUTO_LOCK_H
#define THREADING_MUTEX_AUTO_LOCK_H

#include "threads.h"

#if USE_CPP11_MUTEX
	#include <mutex>
	using MutexAutoLock = std::unique_lock<std::mutex>;
	using RecursiveMutexAutoLock = std::unique_lock<std::recursive_mutex>;
#else

#include "threading/mutex.h"

class MutexAutoLock
{
public:
	MutexAutoLock(Mutex &m) : mutex(m) { mutex.lock(); }
	~MutexAutoLock() { mutex.unlock(); }

private:
	Mutex &mutex;
};

class RecursiveMutexAutoLock
{
public:
	RecursiveMutexAutoLock(RecursiveMutex &m) : mutex(m) { mutex.lock(); }
	~RecursiveMutexAutoLock() { mutex.unlock(); }

private:
	RecursiveMutex &mutex;
};
#endif

#endif


code:
/*
Minetest
Copyright (C) 2013 sapier <sapier AT gmx DOT net>

This program 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 program 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 program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "threading/semaphore.h"

#include <iostream>
#include <cstdlib>
#include <cassert>

#define UNUSED(expr) do { (void)(expr); } while (0)

#ifdef _WIN32
	#include <climits>
	#define MAX_SEMAPHORE_COUNT LONG_MAX - 1
#else
	#include <cerrno>
	#include <sys/time.h>
	#include <pthread.h>
	#if defined(__MACH__) && defined(__APPLE__)
		#include <mach/mach.h>
		#include <mach/task.h>
		#include <mach/semaphore.h>
		#include <sys/semaphore.h>
		#include <unistd.h>

		#undef sem_t
		#undef sem_init
		#undef sem_wait
		#undef sem_post
		#undef sem_destroy
		#define sem_t             semaphore_t
		#define sem_init(s, p, c) semaphore_create(mach_task_self(), (s), 0, (c))
		#define sem_wait(s)       semaphore_wait(*(s))
		#define sem_post(s)       semaphore_signal(*(s))
		#define sem_destroy(s)    semaphore_destroy(mach_task_self(), *(s))
	#endif
#endif

Semaphore::Semaphore(int val)
{
#ifdef _WIN32
	semaphore = CreateSemaphore(NULL, val, MAX_SEMAPHORE_COUNT, NULL);
#else
	int ret = sem_init(&semaphore, 0, val);
	assert(!ret);
	UNUSED(ret);
#endif
}

Semaphore::~Semaphore()
{
#ifdef _WIN32
	CloseHandle(semaphore);
#else
	int ret = sem_destroy(&semaphore);
#ifdef __ANDROID__
	// Workaround for broken bionic semaphore implementation!
	assert(!ret || errno == EBUSY);
#else
	assert(!ret);
#endif
	UNUSED(ret);
#endif
}

void Semaphore::post(unsigned int num)
{
	assert(num > 0);
#ifdef _WIN32
	ReleaseSemaphore(semaphore, num, NULL);
#else
	for (unsigned i = 0; i < num; i++) {
		int ret = sem_post(&semaphore);
		assert(!ret);
		UNUSED(ret);
	}
#endif
}

void Semaphore::wait()
{
#ifdef _WIN32
	WaitForSingleObject(semaphore, INFINITE);
#else
	int ret = sem_wait(&semaphore);
	assert(!ret);
	UNUSED(ret);
#endif
}

bool Semaphore::wait(unsigned int time_ms)
{
#ifdef _WIN32
	unsigned int ret = WaitForSingleObject(semaphore, time_ms);

	if (ret == WAIT_OBJECT_0) {
		return true;
	} else {
		assert(ret == WAIT_TIMEOUT);
		return false;
	}
#else
# if defined(__MACH__) && defined(__APPLE__)
	mach_timespec_t wait_time;
	wait_time.tv_sec = time_ms / 1000;
	wait_time.tv_nsec = 1000000 * (time_ms % 1000);

	errno = 0;
	int ret = semaphore_timedwait(semaphore, wait_time);
	switch (ret) {
	case KERN_OPERATION_TIMED_OUT:
		errno = ETIMEDOUT;
		break;
	case KERN_ABORTED:
		errno = EINTR;
		break;
	default:
		if (ret)
			errno = EINVAL;
	}
# else
	struct timespec wait_time;
	struct timeval now;

	if (gettimeofday(&now, NULL) == -1) {
		std::cerr << "Semaphore::wait(ms): Unable to get time with gettimeofday!" << std::endl;
		abort();
	}

	wait_time.tv_nsec = ((time_ms % 1000) * 1000 * 1000) + (now.tv_usec * 1000);
	wait_time.tv_sec  = (time_ms / 1000) + (wait_time.tv_nsec / (1000 * 1000 * 1000)) + now.tv_sec;
	wait_time.tv_nsec %= 1000 * 1000 * 1000;

	int ret = sem_timedwait(&semaphore, &wait_time);
# endif

	assert(!ret || (errno == ETIMEDOUT || errno == EINTR));
	return !ret;
#endif
}


code:
/*
Minetest
Copyright (C) 2013 sapier <sapier AT gmx DOT net>

This program 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 program 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 program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef THREADING_SEMAPHORE_H
#define THREADING_SEMAPHORE_H

#if defined(_WIN32)
#include <windows.h>
#elif defined(__MACH__) && defined(__APPLE__)
#include <mach/semaphore.h>
#else
#include <semaphore.h>
#endif

#include "util/basic_macros.h"

class Semaphore
{
public:
	Semaphore(int val = 0);
	~Semaphore();

	void post(unsigned int num = 1);
	void wait();
	bool wait(unsigned int time_ms);

private:
#if defined(WIN32)
	HANDLE semaphore;
#elif defined(__MACH__) && defined(__APPLE__)
	semaphore_t semaphore;
#else
	sem_t semaphore;
#endif

	DISABLE_CLASS_COPY(Semaphore);
};

#endif


code:
/*
This file is a part of the JThread package, which contains some object-
oriented thread wrappers for different thread implementations.

Copyright (c) 2000-2006  Jori Liesenborgs (jori.liesenborgs@gmail.com)

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

#include "threading/thread.h"
#include "threading/mutex_auto_lock.h"
#include "log.h"
#include "porting.h"

#define UNUSED(expr) do { (void)(expr); } while (0)

#if USE_CPP11_THREADS
	#include <chrono>
	#include <system_error>
#elif USE_WIN_THREADS
	#ifndef _WIN32_WCE
		#include <process.h>
	#endif
#elif USE_POSIX_THREADS
	#include <time.h>
	#include <assert.h>
	#include <stdlib.h>
	#include <unistd.h>
	#include <sys/time.h>

	#if defined(__FreeBSD__) || defined(__APPLE__)
		#include <sys/types.h>
		#include <sys/sysctl.h>
	#elif defined(_GNU_SOURCE)
		#include <sys/sysinfo.h>
	#endif
#endif

// for setName
#if defined(__linux__)
	#include <sys/prctl.h>
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
	#include <pthread_np.h>
#elif defined(_MSC_VER)
	struct THREADNAME_INFO {
		DWORD dwType;     // Must be 0x1000
		LPCSTR szName;    // Pointer to name (in user addr space)
		DWORD dwThreadID; // Thread ID (-1=caller thread)
		DWORD dwFlags;    // Reserved for future use, must be zero
	};
#endif

// for bindToProcessor
#if __FreeBSD_version >= 702106
	typedef cpuset_t cpu_set_t;
#elif defined(__linux__)
	#include <sched.h>
#elif defined(__sun) || defined(sun)
	#include <sys/types.h>
	#include <sys/processor.h>
	#include <sys/procset.h>
#elif defined(_AIX)
	#include <sys/processor.h>
	#include <sys/thread.h>
#elif defined(__APPLE__)
	#include <mach/mach_init.h>
	#include <mach/thread_act.h>
#endif

Thread::Thread(const std::string &name) :
	m_name(name),
	m_retval(NULL),
	m_joinable(false),
	m_request_stop(false),
	m_running(false)
{
#ifdef _AIX
	m_kernel_thread_id = -1;
#endif
}

Thread::~Thread()
{
	kill();

	// Make sure start finished mutex is unlocked before it's destroyed
	if (m_start_finished_mutex.try_lock())
		m_start_finished_mutex.unlock();

}

bool Thread::start()
{
	MutexAutoLock lock(m_mutex);

	if (m_running)
		return false;

	m_request_stop = false;

	// The mutex may already be locked if the thread is being restarted
	m_start_finished_mutex.try_lock();

#if USE_CPP11_THREADS

	try {
		m_thread_obj = new std::thread(threadProc, this);
	} catch (const std::system_error &e) {
		return false;
	}

#elif USE_WIN_THREADS

	m_thread_handle = CreateThread(NULL, 0, threadProc, this, 0, &m_thread_id);
	if (!m_thread_handle)
		return false;

#elif USE_POSIX_THREADS

	int status = pthread_create(&m_thread_handle, NULL, threadProc, this);
	if (status)
		return false;

#endif

	// Allow spawned thread to continue
	m_start_finished_mutex.unlock();

	while (!m_running)
		sleep_ms(1);

	m_joinable = true;

	return true;
}

bool Thread::stop()
{
	m_request_stop = true;
	return true;
}

bool Thread::wait()
{
	MutexAutoLock lock(m_mutex);

	if (!m_joinable)
		return false;

#if USE_CPP11_THREADS

	m_thread_obj->join();

	delete m_thread_obj;
	m_thread_obj = NULL;

#elif USE_WIN_THREADS

	int ret = WaitForSingleObject(m_thread_handle, INFINITE);
	assert(ret == WAIT_OBJECT_0);
	UNUSED(ret);

	CloseHandle(m_thread_handle);
	m_thread_handle = NULL;
	m_thread_id = -1;

#elif USE_POSIX_THREADS

	int ret = pthread_join(m_thread_handle, NULL);
	assert(ret == 0);
	UNUSED(ret);

#endif

	assert(m_running == false);
	m_joinable = false;
	return true;
}

bool Thread::kill()
{
	if (!m_running) {
		wait();
		return false;
	}

	m_running = false;

#if USE_WIN_THREADS
	TerminateThread(m_thread_handle, 0);
	CloseHandle(m_thread_handle);
#else
	// We need to pthread_kill instead on Android since NDKv5's pthread
	// implementation is incomplete.
# ifdef __ANDROID__
	pthread_kill(getThreadHandle(), SIGKILL);
# else
	pthread_cancel(getThreadHandle());
# endif
	wait();
#endif

	m_retval       = NULL;
	m_joinable     = false;
	m_request_stop = false;

	return true;
}

bool Thread::getReturnValue(void **ret)
{
	if (m_running)
		return false;

	*ret = m_retval;
	return true;
}

#if USE_CPP11_THREADS || USE_POSIX_THREADS
void *Thread::threadProc(void *param)
#elif defined(_WIN32_WCE)
DWORD Thread::threadProc(LPVOID param)
#elif defined(_WIN32)
DWORD WINAPI Thread::threadProc(LPVOID param)
#endif
{
	Thread *thr = (Thread *)param;

#ifdef _AIX
	thr->m_kernel_thread_id = thread_self();
#endif

	thr->setName(thr->m_name);

	g_logger.registerThread(thr->m_name);
	thr->m_running = true;

	// Wait for the thread that started this one to finish initializing the
	// thread handle so that getThreadId/getThreadHandle will work.
	thr->m_start_finished_mutex.lock();

	thr->m_retval = thr->run();

	thr->m_running = false;
	// Unlock m_start_finished_mutex to prevent data race condition on Windows.
	// On Windows with VS2017 build TerminateThread is called and this mutex is not
	// released. We try to unlock it from caller thread and it's refused by system.
	thr->m_start_finished_mutex.unlock();
	g_logger.deregisterThread();

	// 0 is returned here to avoid an unnecessary ifdef clause
	return 0;
}

void Thread::setName(const std::string &name)
{
#if defined(__linux__)

	// It would be cleaner to do this with pthread_setname_np,
	// which was added to glibc in version 2.12, but some major
	// distributions are still runing 2.11 and previous versions.
	prctl(PR_SET_NAME, name.c_str());

#elif defined(__FreeBSD__) || defined(__OpenBSD__)

	pthread_set_name_np(pthread_self(), name.c_str());

#elif defined(__NetBSD__)

	pthread_setname_np(pthread_self(), name.c_str());

#elif defined(__APPLE__)

	pthread_setname_np(name.c_str());

#elif defined(_MSC_VER)

	// Windows itself doesn't support thread names,
	// but the MSVC debugger does...
	THREADNAME_INFO info;

	info.dwType = 0x1000;
	info.szName = name.c_str();
	info.dwThreadID = -1;
	info.dwFlags = 0;

	__try {
		RaiseException(0x406D1388, 0,
			sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info);
	} __except (EXCEPTION_CONTINUE_EXECUTION) {
	}

#elif defined(_WIN32) || defined(__GNU__)

	// These platforms are known to not support thread names.
	// Silently ignore the request.

#else
	#warning "Unrecognized platform, thread names will not be available."
#endif
}

unsigned int Thread::getNumberOfProcessors()
{
#if USE_CPP11_THREADS

	return std::thread::hardware_concurrency();

#elif USE_WIN_THREADS

	SYSTEM_INFO sysinfo;
	GetSystemInfo(&sysinfo);
	return sysinfo.dwNumberOfProcessors;

#elif defined(_SC_NPROCESSORS_ONLN)

	return sysconf(_SC_NPROCESSORS_ONLN);

#elif defined(__FreeBSD__) || defined(__NetBSD__) || \
	defined(__DragonFly__) || defined(__APPLE__)

	unsigned int num_cpus = 1;
	size_t len = sizeof(num_cpus);

	int mib[2];
	mib[0] = CTL_HW;
	mib[1] = HW_NCPU;

	sysctl(mib, 2, &num_cpus, &len, NULL, 0);
	return num_cpus;

#elif defined(_GNU_SOURCE)

	return get_nprocs();

#elif defined(PTW32_VERSION) || defined(__hpux)

	return pthread_num_processors_np();

#else

	return 1;

#endif
}

bool Thread::bindToProcessor(unsigned int proc_number)
{
#if defined(__ANDROID__)

	return false;

#elif USE_WIN_THREADS

	return SetThreadAffinityMask(getThreadHandle(), 1 << proc_number);

#elif __FreeBSD_version >= 702106 || defined(__linux__)

	cpu_set_t cpuset;

	CPU_ZERO(&cpuset);
	CPU_SET(proc_number, &cpuset);

	return pthread_setaffinity_np(getThreadHandle(), sizeof(cpuset), &cpuset) == 0;

#elif defined(__sun) || defined(sun)

	return processor_bind(P_LWPID, P_MYID, proc_number, NULL) == 0

#elif defined(_AIX)

	return bindprocessor(BINDTHREAD, m_kernel_thread_id, proc_number) == 0;

#elif defined(__hpux) || defined(hpux)

	pthread_spu_t answer;

	return pthread_processor_bind_np(PTHREAD_BIND_ADVISORY_NP,
			&answer, proc_number, getThreadHandle()) == 0;

#elif defined(__APPLE__)

	struct thread_affinity_policy tapol;

	thread_port_t threadport = pthread_mach_thread_np(getThreadHandle());
	tapol.affinity_tag = proc_number + 1;
	return thread_policy_set(threadport, THREAD_AFFINITY_POLICY,
			(thread_policy_t)&tapol,
			THREAD_AFFINITY_POLICY_COUNT) == KERN_SUCCESS;

#else

	return false;

#endif
}

bool Thread::setPriority(int prio)
{
#if USE_WIN_THREADS

	return SetThreadPriority(getThreadHandle(), prio);

#else

	struct sched_param sparam;
	int policy;

	if (pthread_getschedparam(getThreadHandle(), &policy, &sparam) != 0)
		return false;

	int min = sched_get_priority_min(policy);
	int max = sched_get_priority_max(policy);

	sparam.sched_priority = min + prio * (max - min) / THREAD_PRIORITY_HIGHEST;
	return pthread_setschedparam(getThreadHandle(), policy, &sparam) == 0;

#endif
}


code:
/*
This file is a part of the JThread package, which contains some object-
oriented thread wrappers for different thread implementations.

Copyright (c) 2000-2006  Jori Liesenborgs (jori.liesenborgs@gmail.com)

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

#ifndef THREADING_THREAD_H
#define THREADING_THREAD_H

#include "util/basic_macros.h"
#include "threading/atomic.h"
#include "threading/mutex.h"
#include "threads.h"

#include <string>
#ifdef _AIX
	#include <sys/thread.h> // for tid_t
#endif

/*
 * On platforms using pthreads, these five priority classes correlate to
 * even divisions between the minimum and maximum reported thread priority.
 */
#if !defined(_WIN32)
	#define THREAD_PRIORITY_LOWEST       0
	#define THREAD_PRIORITY_BELOW_NORMAL 1
	#define THREAD_PRIORITY_NORMAL       2
	#define THREAD_PRIORITY_ABOVE_NORMAL 3
	#define THREAD_PRIORITY_HIGHEST      4
#endif

class Thread {
public:
	Thread(const std::string &name="");
	virtual ~Thread();

	/*
	 * Begins execution of a new thread at the pure virtual method Thread::run().
	 * Execution of the thread is guaranteed to have started after this function
	 * returns.
	 */
	bool start();

	/*
	 * Requests that the thread exit gracefully.
	 * Returns immediately; thread execution is guaranteed to be complete after
	 * a subsequent call to Thread::wait.
	 */
	bool stop();

	/*
	 * Immediately terminates the thread.
	 * This should be used with extreme caution, as the thread will not have
	 * any opportunity to release resources it may be holding (such as memory
	 * or locks).
	 */
	bool kill();

	/*
	 * Waits for thread to finish.
	 * Note:  This does not stop a thread, you have to do this on your own.
	 * Returns false immediately if the thread is not started or has been waited
	 * on before.
	 */
	bool wait();

	/*
	 * Returns true if the calling thread is this Thread object.
	 */
	bool isCurrentThread() { return thr_is_current_thread(getThreadId()); }

	inline bool isRunning() { return m_running; }
	inline bool stopRequested() { return m_request_stop; }

#if USE_CPP11_THREADS
	inline threadid_t getThreadId() { return m_thread_obj->get_id(); }
	inline threadhandle_t getThreadHandle() { return m_thread_obj->native_handle(); }
#else
#  if USE_WIN_THREADS
	inline threadid_t getThreadId() { return m_thread_id; }
#  else
	inline threadid_t getThreadId() { return m_thread_handle; }
#  endif
	inline threadhandle_t getThreadHandle() { return m_thread_handle; }
#endif

	/*
	 * Gets the thread return value.
	 * Returns true if the thread has exited and the return value was available,
	 * or false if the thread has yet to finish.
	 */
	bool getReturnValue(void **ret);

	/*
	 * Binds (if possible, otherwise sets the affinity of) the thread to the
	 * specific processor specified by proc_number.
	 */
	bool bindToProcessor(unsigned int proc_number);

	/*
	 * Sets the thread priority to the specified priority.
	 *
	 * prio can be one of: THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL,
	 * THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST.
	 * On Windows, any of the other priorites as defined by SetThreadPriority
	 * are supported as well.
	 *
	 * Note that it may be necessary to first set the threading policy or
	 * scheduling algorithm to one that supports thread priorities if not
	 * supported by default, otherwise this call will have no effect.
	 */
	bool setPriority(int prio);

	/*
	 * Sets the currently executing thread's name to where supported; useful
	 * for debugging.
	 */
	static void setName(const std::string &name);

	/*
	 * Returns the number of processors/cores configured and active on this machine.
	 */
	static unsigned int getNumberOfProcessors();

protected:
	std::string m_name;

	virtual void *run() = 0;

private:
	void *m_retval;
	bool m_joinable;
	Atomic<bool> m_request_stop;
	Atomic<bool> m_running;
	Mutex m_mutex;
	Mutex m_start_finished_mutex;

#if USE_CPP11_THREADS
	std::thread *m_thread_obj;
#else
	threadhandle_t m_thread_handle;
#   if USE_WIN_THREADS
        threadid_t m_thread_id;
#   endif
#endif

	static ThreadStartFunc threadProc;

#ifdef _AIX
	// For AIX, there does not exist any mapping from pthread_t to tid_t
	// available to us, so we maintain one ourselves.  This is set on thread start.
	tid_t m_kernel_thread_id;
#endif

	DISABLE_CLASS_COPY(Thread);
};

#endif


code:
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program 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 program 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 program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef THREADS_HEADER
#define THREADS_HEADER

//
// Determine which threading APIs we will use
//
#if __cplusplus >= 201103L
	#define USE_CPP11_THREADS 1
#elif defined(_WIN32)
	#define USE_WIN_THREADS 1
#else
	#define USE_POSIX_THREADS 1
#endif

#if defined(_WIN32)
	// Prefer critical section API because std::mutex is much slower on Windows
	#define USE_WIN_MUTEX 1
#elif __cplusplus >= 201103L
	#define USE_CPP11_MUTEX 1
#else
	#define USE_POSIX_MUTEX 1
#endif

///////////////

#if USE_CPP11_THREADS
	#include <thread>
#elif USE_POSIX_THREADS
	#include <pthread.h>
#else
	#ifndef WIN32_LEAN_AND_MEAN
		#define WIN32_LEAN_AND_MEAN
	#endif
	#include <windows.h>
#endif

#include "threading/mutex.h"

//
// threadid_t, threadhandle_t
//
#if USE_CPP11_THREADS
	typedef std::thread::id threadid_t;
	typedef std::thread::native_handle_type threadhandle_t;
#elif USE_WIN_THREADS
	typedef DWORD threadid_t;
	typedef HANDLE threadhandle_t;
#elif USE_POSIX_THREADS
	typedef pthread_t threadid_t;
	typedef pthread_t threadhandle_t;
#endif

//
// ThreadStartFunc
//
#if USE_CPP11_THREADS || USE_POSIX_THREADS
	typedef void *ThreadStartFunc(void *param);
#elif defined(_WIN32_WCE)
	typedef DWORD ThreadStartFunc(LPVOID param);
#elif defined(_WIN32)
	typedef DWORD WINAPI ThreadStartFunc(LPVOID param);
#endif

inline threadid_t thr_get_current_thread_id()
{
#if USE_CPP11_THREADS
	return std::this_thread::get_id();
#elif USE_WIN_THREADS
	return GetCurrentThreadId();
#elif USE_POSIX_THREADS
	return pthread_self();
#endif
}

inline bool thr_compare_thread_id(threadid_t thr1, threadid_t thr2)
{
#if USE_POSIX_THREADS
	return pthread_equal(thr1, thr2);
#else
	return thr1 == thr2;
#endif
}

inline bool thr_is_current_thread(threadid_t thr)
{
	return thr_compare_thread_id(thr_get_current_thread_id(), thr);
}

#endif


code:
/*
Minetest
Copyright (C) 2016 sfan5 <sfan5@live.de>

This program 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 program 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 program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "tileanimation.h"
#include "util/serialize.h"

void TileAnimationParams::serialize(std::ostream &os, u16 protocol_version) const
{
	if (protocol_version < 29) {
		if (type == TAT_VERTICAL_FRAMES) {
			writeU8(os, type);
			writeU16(os, vertical_frames.aspect_w);
			writeU16(os, vertical_frames.aspect_h);
			writeF1000(os, vertical_frames.length);
		} else {
			writeU8(os, TAT_NONE);
			writeU16(os, 1);
			writeU16(os, 1);
			writeF1000(os, 1.0);
		}
		return;
	}

	writeU8(os, type);
	if (type == TAT_VERTICAL_FRAMES) {
		writeU16(os, vertical_frames.aspect_w);
		writeU16(os, vertical_frames.aspect_h);
		writeF1000(os, vertical_frames.length);
	} else if (type == TAT_SHEET_2D) {
		writeU8(os, sheet_2d.frames_w);
		writeU8(os, sheet_2d.frames_h);
		writeF1000(os, sheet_2d.frame_length);
	}
}

void TileAnimationParams::deSerialize(std::istream &is, u16 protocol_version)
{
	type = (TileAnimationType) readU8(is);
	if (protocol_version < 29) {
		vertical_frames.aspect_w = readU16(is);
		vertical_frames.aspect_h = readU16(is);
		vertical_frames.length = readF1000(is);
		return;
	}

	if (type == TAT_VERTICAL_FRAMES) {
		vertical_frames.aspect_w = readU16(is);
		vertical_frames.aspect_h = readU16(is);
		vertical_frames.length = readF1000(is);
	} else if (type == TAT_SHEET_2D) {
		sheet_2d.frames_w = readU8(is);
		sheet_2d.frames_h = readU8(is);
		sheet_2d.frame_length = readF1000(is);
	}
}

void TileAnimationParams::determineParams(v2u32 texture_size, int *frame_count,
		int *frame_length_ms, v2u32 *frame_size) const
{
	if (type == TAT_VERTICAL_FRAMES) {
		int frame_height = (float)texture_size.X /
				(float)vertical_frames.aspect_w *
				(float)vertical_frames.aspect_h;
		int _frame_count = texture_size.Y / frame_height;
		if (frame_count)
			*frame_count = _frame_count;
		if (frame_length_ms)
			*frame_length_ms = 1000.0 * vertical_frames.length / _frame_count;
		if (frame_size)
			*frame_size = v2u32(texture_size.X, frame_height);
	} else if (type == TAT_SHEET_2D) {
		if (frame_count)
			*frame_count = sheet_2d.frames_w * sheet_2d.frames_h;
		if (frame_length_ms)
			*frame_length_ms = 1000 * sheet_2d.frame_length;
		if (frame_size)
			*frame_size = v2u32(texture_size.X / sheet_2d.frames_w, texture_size.Y / sheet_2d.frames_h);
	}
	// caller should check for TAT_NONE
}

void TileAnimationParams::getTextureModifer(std::ostream &os, v2u32 texture_size, int frame) const
{
	if (type == TAT_NONE)
		return;
	if (type == TAT_VERTICAL_FRAMES) {
		int frame_count;
		determineParams(texture_size, &frame_count, NULL, NULL);
		os << "^[verticalframe:" << frame_count << ":" << frame;
	} else if (type == TAT_SHEET_2D) {
		int q, r;
		q = frame / sheet_2d.frames_w;
		r = frame % sheet_2d.frames_w;
		os << "^[sheet:" << sheet_2d.frames_w << "x" << sheet_2d.frames_h
			<< ":" << r << "," << q;
	}
}

v2f TileAnimationParams::getTextureCoords(v2u32 texture_size, int frame) const
{
	v2u32 ret(0, 0);
	if (type == TAT_VERTICAL_FRAMES) {
		int frame_height = (float)texture_size.X /
				(float)vertical_frames.aspect_w *
				(float)vertical_frames.aspect_h;
		ret = v2u32(0, frame_height * frame);
	} else if (type == TAT_SHEET_2D) {
		v2u32 frame_size;
		determineParams(texture_size, NULL, NULL, &frame_size);
		int q, r;
		q = frame / sheet_2d.frames_w;
		r = frame % sheet_2d.frames_w;
		ret = v2u32(r * frame_size.X, q * frame_size.Y);
	}
	return v2f(ret.X / (float) texture_size.X, ret.Y / (float) texture_size.Y);
}


code:
/*
Minetest
Copyright (C) 2016 sfan5 <sfan5@live.de>

This program 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 program 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 program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef TILEANIMATION_HEADER
#define TILEANIMATION_HEADER

#include <iostream>
#include "irrlichttypes_bloated.h"

enum TileAnimationType
{
	TAT_NONE = 0,
	TAT_VERTICAL_FRAMES = 1,
	TAT_SHEET_2D = 2,
};

struct TileAnimationParams
{
	enum TileAnimationType type;
	union
	{
		// struct {
		// } none;
		struct
		{
			int aspect_w; // width for aspect ratio
			int aspect_h; // height for aspect ratio
			float length; // seconds
		} vertical_frames;
		struct
		{
			int frames_w;       // number of frames left-to-right
			int frames_h;       // number of frames top-to-bottom
			float frame_length; // seconds
		} sheet_2d;
	};

	void serialize(std::ostream &os, u16 protocol_version) const;
	void deSerialize(std::istream &is, u16 protocol_version);
	void determineParams(v2u32 texture_size, int *frame_count, int *frame_length_ms,
			v2u32 *frame_size) const;
	void getTextureModifer(std::ostream &os, v2u32 texture_size, int frame) const;
	v2f getTextureCoords(v2u32 texture_size, int frame) const;
};

#endif


code:
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program 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 program 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 program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "tool.h"
#include "itemgroup.h"
#include "log.h"
#include "inventory.h"
#include "exceptions.h"
#include "util/serialize.h"
#include "util/numeric.h"

void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const
{
	writeU8(os, 2); // version (protocol >= 18)
	writeF1000(os, full_punch_interval);
	writeS16(os, max_drop_level);
	writeU32(os, groupcaps.size());
	for (ToolGCMap::const_iterator i = groupcaps.begin(); i != groupcaps.end(); ++i) {
		const std::string *name = &i->first;
		const ToolGroupCap *cap = &i->second;
		os << serializeString(*name);
		writeS16(os, cap->uses);
		writeS16(os, cap->maxlevel);
		writeU32(os, cap->times.size());
		for (UNORDERED_MAP<int, float>::const_iterator
				j = cap->times.begin(); j != cap->times.end(); ++j) {
			writeS16(os, j->first);
			writeF1000(os, j->second);
		}
	}

	writeU32(os, damageGroups.size());

	for (DamageGroup::const_iterator i = damageGroups.begin();
			i != damageGroups.end(); ++i) {
		os << serializeString(i->first);
		writeS16(os, i->second);
	}
}

void ToolCapabilities::deSerialize(std::istream &is)
{
	int version = readU8(is);
	if(version != 1 && version != 2) throw SerializationError(
			"unsupported ToolCapabilities version");
	full_punch_interval = readF1000(is);
	max_drop_level = readS16(is);
	groupcaps.clear();
	u32 groupcaps_size = readU32(is);
	for(u32 i=0; i<groupcaps_size; i++){
		std::string name = deSerializeString(is);
		ToolGroupCap cap;
		cap.uses = readS16(is);
		cap.maxlevel = readS16(is);
		u32 times_size = readU32(is);
		for(u32 i=0; i<times_size; i++){
			int level = readS16(is);
			float time = readF1000(is);
			cap.times[level] = time;
		}
		groupcaps[name] = cap;
	}
	if(version == 2)
	{
		u32 damage_groups_size = readU32(is);
		for(u32 i=0; i<damage_groups_size; i++){
			std::string name = deSerializeString(is);
			s16 rating = readS16(is);
			damageGroups[name] = rating;
		}
	}
}

DigParams getDigParams(const ItemGroupList &groups,
		const ToolCapabilities *tp, float time_from_last_punch)
{
	//infostream<<"getDigParams"<<std::endl;
	/* Check group dig_immediate */
	switch(itemgroup_get(groups, "dig_immediate")){
	case 2:
		//infostream<<"dig_immediate=2"<<std::endl;
		return DigParams(true, 0.5, 0, "dig_immediate");
	case 3:
		//infostream<<"dig_immediate=3"<<std::endl;
		return DigParams(true, 0, 0, "dig_immediate");
	default:
		break;
	}

	// Values to be returned (with a bit of conversion)
	bool result_diggable = false;
	float result_time = 0.0;
	float result_wear = 0.0;
	std::string result_main_group = "";

	int level = itemgroup_get(groups, "level");
	//infostream<<"level="<<level<<std::endl;
	for (ToolGCMap::const_iterator i = tp->groupcaps.begin();
		 	i != tp->groupcaps.end(); ++i) {
		const std::string &name = i->first;
		//infostream<<"group="<<name<<std::endl;
		const ToolGroupCap &cap = i->second;
		int rating = itemgroup_get(groups, name);
		float time = 0;
		bool time_exists = cap.getTime(rating, &time);
		if(!result_diggable || time < result_time){
			if(cap.maxlevel >= level && time_exists){
				result_diggable = true;
				int leveldiff = cap.maxlevel - level;
				result_time = time / MYMAX(1, leveldiff);
				if(cap.uses != 0)
					result_wear = 1.0 / cap.uses / pow(3.0, (double)leveldiff);
				else
					result_wear = 0;
				result_main_group = name;
			}
		}
	}
	//infostream<<"result_diggable="<<result_diggable<<std::endl;
	//infostream<<"result_time="<<result_time<<std::endl;
	//infostream<<"result_wear="<<result_wear<<std::endl;

	if(time_from_last_punch < tp->full_punch_interval){
		float f = time_from_last_punch / tp->full_punch_interval;
		//infostream<<"f="<<f<<std::endl;
		result_time /= f;
		result_wear /= f;
	}

	u16 wear_i = 65535.*result_wear;
	return DigParams(result_diggable, result_time, wear_i, result_main_group);
}

DigParams getDigParams(const ItemGroupList &groups,
		const ToolCapabilities *tp)
{
	return getDigParams(groups, tp, 1000000);
}

HitParams getHitParams(const ItemGroupList &armor_groups,
		const ToolCapabilities *tp, float time_from_last_punch)
{
	s16 damage = 0;
	float full_punch_interval = tp->full_punch_interval;

	for (DamageGroup::const_iterator i = tp->damageGroups.begin();
			i != tp->damageGroups.end(); ++i) {
		s16 armor = itemgroup_get(armor_groups, i->first);
		damage += i->second * rangelim(time_from_last_punch / full_punch_interval, 0.0, 1.0)
				* armor / 100.0;
	}

	return HitParams(damage, 0);
}

HitParams getHitParams(const ItemGroupList &armor_groups,
		const ToolCapabilities *tp)
{
	return getHitParams(armor_groups, tp, 1000000);
}

PunchDamageResult getPunchDamage(
		const ItemGroupList &armor_groups,
		const ToolCapabilities *toolcap,
		const ItemStack *punchitem,
		float time_from_last_punch
){
	bool do_hit = true;
	{
		if(do_hit && punchitem){
			if(itemgroup_get(armor_groups, "punch_operable") &&
					(toolcap == NULL || punchitem->name == ""))
				do_hit = false;
		}
		if(do_hit){
			if(itemgroup_get(armor_groups, "immortal"))
				do_hit = false;
		}
	}

	PunchDamageResult result;
	if(do_hit)
	{
		HitParams hitparams = getHitParams(armor_groups, toolcap,
				time_from_last_punch);
		result.did_punch = true;
		result.wear = hitparams.wear;
		result.damage = hitparams.hp;
	}

	return result;
}


code:
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program 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 program 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 program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef TOOL_HEADER
#define TOOL_HEADER

#include "irrlichttypes.h"
#include <string>
#include <iostream>
#include "util/cpp11_container.h"
#include "itemgroup.h"

struct ToolGroupCap
{
	UNORDERED_MAP<int, float> times;
	int maxlevel;
	int uses;

	ToolGroupCap():
		maxlevel(1),
		uses(20)
	{}

	bool getTime(int rating, float *time) const
	{
		UNORDERED_MAP<int, float>::const_iterator i = times.find(rating);
		if (i == times.end()) {
			*time = 0;
			return false;
		}
		*time = i->second;
		return true;
	}
};

typedef UNORDERED_MAP<std::string, struct ToolGroupCap> ToolGCMap;
typedef UNORDERED_MAP<std::string, s16> DamageGroup;

struct ToolCapabilities
{
	float full_punch_interval;
	int max_drop_level;
	ToolGCMap groupcaps;
	DamageGroup damageGroups;

	ToolCapabilities(
			float full_punch_interval_=1.4,
			int max_drop_level_=1,
			const ToolGCMap &groupcaps_ = ToolGCMap(),
			const DamageGroup &damageGroups_ = DamageGroup()
	):
		full_punch_interval(full_punch_interval_),
		max_drop_level(max_drop_level_),
		groupcaps(groupcaps_),
		damageGroups(damageGroups_)
	{}

	void serialize(std::ostream &os, u16 version) const;
	void deSerialize(std::istream &is);
};

struct DigParams
{
	bool diggable;
	// Digging time in seconds
	float time;
	// Caused wear
	u16 wear;
	std::string main_group;

	DigParams(bool a_diggable = false, float a_time = 0.0f, u16 a_wear = 0,
			const std::string &a_main_group = ""):
		diggable(a_diggable),
		time(a_time),
		wear(a_wear),
		main_group(a_main_group)
	{}
};

DigParams getDigParams(const ItemGroupList &groups,
		const ToolCapabilities *tp, float time_from_last_punch);

DigParams getDigParams(const ItemGroupList &groups,
		const ToolCapabilities *tp);

struct HitParams
{
	s16 hp;
	s16 wear;

	HitParams(s16 hp_=0, s16 wear_=0):
		hp(hp_),
		wear(wear_)
	{}
};

HitParams getHitParams(const ItemGroupList &armor_groups,
		const ToolCapabilities *tp, float time_from_last_punch);

HitParams getHitParams(const ItemGroupList &armor_groups,
		const ToolCapabilities *tp);

struct PunchDamageResult
{
	bool did_punch;
	int damage;
	int wear;

	PunchDamageResult():
		did_punch(false),
		damage(0),
		wear(0)
	{}
};

struct ItemStack;

PunchDamageResult getPunchDamage(
		const ItemGroupList &armor_groups,
		const ToolCapabilities *toolcap,
		const ItemStack *punchitem,
		float time_from_last_punch
);

#endif


code:
/*
Copyright (C) 2014 sapier

This program 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 program 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 program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "touchscreengui.h"
#include "irrlichttypes.h"
#include "irr_v2d.h"
#include "log.h"
#include "keycode.h"
#include "settings.h"
#include "gettime.h"
#include "util/numeric.h"
#include "porting.h"
#include "guiscalingfilter.h"

#include <iostream>
#include <algorithm>

#include <ISceneCollisionManager.h>

// Very slow button repeat frequency (in seconds)
#define SLOW_BUTTON_REPEAT	(1.0f)

using namespace irr::core;

extern Settings *g_settings;

const char** touchgui_button_imagenames = (const char*[]) {
	"up_arrow.png",
	"down_arrow.png",
	"left_arrow.png",
	"right_arrow.png",
	"jump_btn.png",
	"down.png"
};

static irr::EKEY_CODE id2keycode(touch_gui_button_id id)
{
	std::string key = "";
	switch (id) {
		case forward_id:
			key = "forward";
			break;
		case left_id:
			key = "left";
			break;
		case right_id:
			key = "right";
			break;
		case backward_id:
			key = "backward";
			break;
		case inventory_id:
			key = "inventory";
			break;
		case drop_id:
			key = "drop";
			break;
		case jump_id:
			key = "jump";
			break;
		case crunch_id:
			key = "sneak";
			break;
		case fly_id:
			key = "freemove";
			break;
		case noclip_id:
			key = "noclip";
			break;
		case fast_id:
			key = "fastmove";
			break;
		case debug_id:
			key = "toggle_debug";
			break;
		case chat_id:
			key = "chat";
			break;
		case camera_id:
			key = "camera_mode";
			break;
		case range_id:
			key = "rangeselect";
			break;
	}
	assert(key != "");
	return keyname_to_keycode(g_settings->get("keymap_" + key).c_str());
}

TouchScreenGUI *g_touchscreengui;

static void load_button_texture(button_info* btn, const char* path,
		rect<s32> button_rect, ISimpleTextureSource* tsrc, video::IVideoDriver *driver)
{
	unsigned int tid;
	video::ITexture *texture = guiScalingImageButton(driver,
			tsrc->getTexture(path, &tid), button_rect.getWidth(),
			button_rect.getHeight());
	if (texture) {
		btn->guibutton->setUseAlphaChannel(true);
		if (g_settings->getBool("gui_scaling_filter")) {
			rect<s32> txr_rect = rect<s32>(0, 0, button_rect.getWidth(), button_rect.getHeight());
			btn->guibutton->setImage(texture, txr_rect);
			btn->guibutton->setPressedImage(texture, txr_rect);
			btn->guibutton->setScaleImage(false);
		} else {
			btn->guibutton->setImage(texture);
			btn->guibutton->setPressedImage(texture);
			btn->guibutton->setScaleImage(true);
		}
		btn->guibutton->setDrawBorder(false);
		btn->guibutton->setText(L"");
		}
}

AutoHideButtonBar::AutoHideButtonBar(IrrlichtDevice *device,
		IEventReceiver* receiver) :
			m_texturesource(NULL),
			m_driver(device->getVideoDriver()),
			m_guienv(device->getGUIEnvironment()),
			m_receiver(receiver),
			m_active(false),
			m_visible(true),
			m_timeout(0),
			m_timeout_value(3),
			m_initialized(false),
			m_dir(AHBB_Dir_Right_Left)
{
	m_screensize = device->getVideoDriver()->getScreenSize();

}

void AutoHideButtonBar::init(ISimpleTextureSource* tsrc,
		const char* starter_img, int button_id, v2s32 UpperLeft,
		v2s32 LowerRight, autohide_button_bar_dir dir, float timeout)
{
	m_texturesource = tsrc;

	m_upper_left = UpperLeft;
	m_lower_right = LowerRight;

	/* init settings bar */

	irr::core::rect<int> current_button = rect<s32>(UpperLeft.X, UpperLeft.Y,
			LowerRight.X, LowerRight.Y);

	m_starter.guibutton         = m_guienv->addButton(current_button, 0, button_id, L"", 0);
	m_starter.guibutton->grab();
	m_starter.repeatcounter     = -1;
	m_starter.keycode           = KEY_OEM_8; // use invalid keycode as it's not relevant
	m_starter.immediate_release = true;
	m_starter.ids.clear();

	load_button_texture(&m_starter, starter_img, current_button,
			m_texturesource, m_driver);

	m_dir = dir;
	m_timeout_value = timeout;

	m_initialized = true;
}

AutoHideButtonBar::~AutoHideButtonBar()
{
	if (m_starter.guibutton) {
		m_starter.guibutton->setVisible(false);
		m_starter.guibutton->drop();
	}
}

void AutoHideButtonBar::addButton(touch_gui_button_id button_id,
		const wchar_t* caption, const char* btn_image)
{

	if (!m_initialized) {
		errorstream << "AutoHideButtonBar::addButton not yet initialized!"
				<< std::endl;
		return;
	}
	int button_size = 0;

	if ((m_dir == AHBB_Dir_Top_Bottom) || (m_dir == AHBB_Dir_Bottom_Top)) {
		button_size = m_lower_right.X - m_upper_left.X;
	} else {
		button_size = m_lower_right.Y - m_upper_left.Y;
	}

	irr::core::rect<int> current_button;

	if ((m_dir == AHBB_Dir_Right_Left) || (m_dir == AHBB_Dir_Left_Right)) {

		int x_start = 0;
		int x_end = 0;

		if (m_dir == AHBB_Dir_Left_Right) {
			x_start = m_lower_right.X + (button_size * 1.25 * m_buttons.size())
					+ (button_size * 0.25);
			x_end = x_start + button_size;
		} else {
			x_end = m_upper_left.X - (button_size * 1.25 * m_buttons.size())
					- (button_size * 0.25);
			x_start = x_end - button_size;
		}

		current_button = rect<s32>(x_start, m_upper_left.Y, x_end,
				m_lower_right.Y);
	} else {
		int y_start = 0;
		int y_end = 0;

		if (m_dir == AHBB_Dir_Top_Bottom) {
			y_start = m_lower_right.X + (button_size * 1.25 * m_buttons.size())
					+ (button_size * 0.25);
			y_end = y_start + button_size;
		} else {
			y_end = m_upper_left.X - (button_size * 1.25 * m_buttons.size())
					- (button_size * 0.25);
			y_start = y_end - button_size;
		}

		current_button = rect<s32>(m_upper_left.X, y_start, m_lower_right.Y,
				y_end);
	}

	button_info* btn       = new button_info();
	btn->guibutton         = m_guienv->addButton(current_button, 0, button_id, caption, 0);
	btn->guibutton->grab();
	btn->guibutton->setVisible(false);
	btn->guibutton->setEnabled(false);
	btn->repeatcounter     = -1;
	btn->keycode           = id2keycode(button_id);
	btn->immediate_release = true;
	btn->ids.clear();

	load_button_texture(btn, btn_image, current_button, m_texturesource,
			m_driver);

	m_buttons.push_back(btn);
}

bool AutoHideButtonBar::isButton(const SEvent &event)
{
	IGUIElement* rootguielement = m_guienv->getRootGUIElement();

	if (rootguielement == NULL) {
		return false;
	}

	gui::IGUIElement *element = rootguielement->getElementFromPoint(
			core::position2d<s32>(event.TouchInput.X, event.TouchInput.Y));

	if (element == NULL) {
		return false;
	}

	if (m_active) {
		/* check for all buttons in vector */

		std::vector<button_info*>::iterator iter = m_buttons.begin();

		while (iter != m_buttons.end()) {
			if ((*iter)->guibutton == element) {

				SEvent* translated = new SEvent();
				memset(translated, 0, sizeof(SEvent));
				translated->EventType            = irr::EET_KEY_INPUT_EVENT;
				translated->KeyInput.Key         = (*iter)->keycode;
				translated->KeyInput.Control     = false;
				translated->KeyInput.Shift       = false;
				translated->KeyInput.Char        = 0;

				/* add this event */
				translated->KeyInput.PressedDown = true;
				m_receiver->OnEvent(*translated);

				/* remove this event */
				translated->KeyInput.PressedDown = false;
				m_receiver->OnEvent(*translated);

				delete translated;

				(*iter)->ids.push_back(event.TouchInput.ID);

				m_timeout = 0;

				return true;
			}
			++iter;
		}
	} else {
		/* check for starter button only */
		if (element == m_starter.guibutton) {
			m_starter.ids.push_back(event.TouchInput.ID);
			m_starter.guibutton->setVisible(false);
			m_starter.guibutton->setEnabled(false);
			m_active = true;
			m_timeout = 0;

			std::vector<button_info*>::iterator iter = m_buttons.begin();

			while (iter != m_buttons.end()) {
				(*iter)->guibutton->setVisible(true);
				(*iter)->guibutton->setEnabled(true);
				++iter;
			}

			return true;
		}
	}
	return false;
}

bool AutoHideButtonBar::isReleaseButton(int eventID)
{
	std::vector<int>::iterator id = std::find(m_starter.ids.begin(),
			m_starter.ids.end(), eventID);

	if (id != m_starter.ids.end()) {
		m_starter.ids.erase(id);
		return true;
	}

	std::vector<button_info*>::iterator iter = m_buttons.begin();

	while (iter != m_buttons.end()) {
		std::vector<int>::iterator id = std::find((*iter)->ids.begin(),
				(*iter)->ids.end(), eventID);

		if (id != (*iter)->ids.end()) {
			(*iter)->ids.erase(id);
			// TODO handle settings button release
			return true;
		}
		++iter;
	}

	return false;
}

void AutoHideButtonBar::step(float dtime)
{
	if (m_active) {
		m_timeout += dtime;

		if (m_timeout > m_timeout_value) {
			deactivate();
		}
	}
}

void AutoHideButtonBar::deactivate()
{
	if (m_visible == true) {
		m_starter.guibutton->setVisible(true);
		m_starter.guibutton->setEnabled(true);
	}
	m_active = false;

	std::vector<button_info*>::iterator iter = m_buttons.begin();

	while (iter != m_buttons.end()) {
			(*iter)->guibutton->setVisible(false);
			(*iter)->guibutton->setEnabled(false);
		++iter;
	}
}

void AutoHideButtonBar::hide()
{
	m_visible = false;
	m_starter.guibutton->setVisible(false);
	m_starter.guibutton->setEnabled(false);

	std::vector<button_info*>::iterator iter = m_buttons.begin();

	while (iter != m_buttons.end()) {
		(*iter)->guibutton->setVisible(false);
		(*iter)->guibutton->setEnabled(false);
		++iter;
	}
}

void AutoHideButtonBar::show()
{
	m_visible = true;

	if (m_active) {
		std::vector<button_info*>::iterator iter = m_buttons.begin();

		while (iter != m_buttons.end()) {
			(*iter)->guibutton->setVisible(true);
			(*iter)->guibutton->setEnabled(true);
			++iter;
		}
	} else {
		m_starter.guibutton->setVisible(true);
		m_starter.guibutton->setEnabled(true);
	}
}

TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver* receiver):
	m_device(device),
	m_guienv(device->getGUIEnvironment()),
	m_camera_yaw_change(0.0),
	m_camera_pitch(0.0),
	m_visible(false),
	m_move_id(-1),
	m_receiver(receiver),
	m_move_has_really_moved(false),
	m_move_downtime(0),
	m_move_sent_as_mouse_event(false),
	// use some downlocation way off screen as init value  to avoid invalid behaviour
	m_move_downlocation(v2s32(-10000, -10000)),
	m_settingsbar(device, receiver),
	m_rarecontrolsbar(device, receiver)
{
	for (unsigned int i=0; i < after_last_element_id; i++) {
		m_buttons[i].guibutton     =  0;
		m_buttons[i].repeatcounter = -1;
		m_buttons[i].repeatdelay   = BUTTON_REPEAT_DELAY;
	}

	m_screensize = m_device->getVideoDriver()->getScreenSize();
}

void TouchScreenGUI::initButton(touch_gui_button_id id, rect<s32> button_rect,
		std::wstring caption, bool immediate_release, float repeat_delay)
{

	button_info* btn       = &m_buttons[id];
	btn->guibutton         = m_guienv->addButton(button_rect, 0, id, caption.c_str());
	btn->guibutton->grab();
	btn->repeatcounter     = -1;
	btn->repeatdelay       = repeat_delay;
	btn->keycode           = id2keycode(id);
	btn->immediate_release = immediate_release;
	btn->ids.clear();

	load_button_texture(btn,touchgui_button_imagenames[id],button_rect,
			m_texturesource, m_device->getVideoDriver());
}

static int getMaxControlPadSize(float density) {
	return 200 * density * g_settings->getFloat("hud_scaling");
}

int TouchScreenGUI::getGuiButtonSize()
{
	u32 control_pad_size = MYMIN((2 * m_screensize.Y) / 3,
			getMaxControlPadSize(porting::getDisplayDensity()));

	return control_pad_size / 3;
}

void TouchScreenGUI::init(ISimpleTextureSource* tsrc)
{
	assert(tsrc != 0);

	u32 button_size      = getGuiButtonSize();
	m_visible            = true;
	m_texturesource      = tsrc;
	m_control_pad_rect   = rect<s32>(0, m_screensize.Y - 3 * button_size,
			3 * button_size, m_screensize.Y);
	/*
	draw control pad
	0 1 2
	3 4 5
	for now only 0, 1, 2, and 4 are used
	*/
	int number = 0;
	for (int y = 0; y < 2; ++y)
		for (int x = 0; x < 3; ++x, ++number) {
			rect<s32> button_rect(
					x * button_size, m_screensize.Y - button_size * (2 - y),
					(x + 1) * button_size, m_screensize.Y - button_size * (1 - y)
			);
			touch_gui_button_id id = after_last_element_id;
			std::wstring caption;
			switch (number) {
			case 0:
				id = left_id;
				caption = L"<";
				break;
			case 1:
				id = forward_id;
				caption = L"^";
				break;
			case 2:
				id = right_id;
				caption = L">";
				break;
			case 4:
				id = backward_id;
				caption = L"v";
				break;
			}
			if (id != after_last_element_id) {
				initButton(id, button_rect, caption, false);
				}
		}

	/* init jump button */
	initButton(jump_id,
			rect<s32>(m_screensize.X-(1.75*button_size),
					m_screensize.Y - (0.5*button_size),
					m_screensize.X-(0.25*button_size),
					m_screensize.Y),
			L"x",false);

	/* init crunch button */
	initButton(crunch_id,
			rect<s32>(m_screensize.X-(3.25*button_size),
					m_screensize.Y - (0.5*button_size),
					m_screensize.X-(1.75*button_size),
					m_screensize.Y),
			L"H",false);

	m_settingsbar.init(m_texturesource, "gear_icon.png", settings_starter_id,
			v2s32(m_screensize.X - (button_size / 2),
					m_screensize.Y - ((SETTINGS_BAR_Y_OFFSET + 1) * button_size)
							+ (button_size * 0.5)),
			v2s32(m_screensize.X,
					m_screensize.Y - (SETTINGS_BAR_Y_OFFSET * button_size)
							+ (button_size * 0.5)), AHBB_Dir_Right_Left,
			3.0);

	m_settingsbar.addButton(fly_id,    L"fly",       "fly_btn.png");
	m_settingsbar.addButton(noclip_id, L"noclip",    "noclip_btn.png");
	m_settingsbar.addButton(fast_id,   L"fast",      "fast_btn.png");
	m_settingsbar.addButton(debug_id,  L"debug",     "debug_btn.png");
	m_settingsbar.addButton(camera_id, L"camera",    "camera_btn.png");
	m_settingsbar.addButton(range_id,  L"rangeview", "rangeview_btn.png");

	m_rarecontrolsbar.init(m_texturesource, "rare_controls.png",
			rare_controls_starter_id,
			v2s32(0,
					m_screensize.Y
							- ((RARE_CONTROLS_BAR_Y_OFFSET + 1) * button_size)
							+ (button_size * 0.5)),
			v2s32(button_size / 2,
					m_screensize.Y - (RARE_CONTROLS_BAR_Y_OFFSET * button_size)
							+ (button_size * 0.5)), AHBB_Dir_Left_Right,
			2);

	m_rarecontrolsbar.addButton(chat_id,      L"Chat", "chat_btn.png");
	m_rarecontrolsbar.addButton(inventory_id, L"inv",  "inventory_btn.png");
	m_rarecontrolsbar.addButton(drop_id,      L"drop", "drop_btn.png");

}

touch_gui_button_id TouchScreenGUI::getButtonID(s32 x, s32 y)
{
	IGUIElement* rootguielement = m_guienv->getRootGUIElement();

	if (rootguielement != NULL) {
		gui::IGUIElement *element =
				rootguielement->getElementFromPoint(core::position2d<s32>(x,y));

		if (element) {
			for (unsigned int i=0; i < after_last_element_id; i++) {
				if (element == m_buttons[i].guibutton) {
					return (touch_gui_button_id) i;
				}
			}
		}
	}
	return after_last_element_id;
}

touch_gui_button_id TouchScreenGUI::getButtonID(int eventID)
{
	for (unsigned int i=0; i < after_last_element_id; i++) {
		button_info* btn = &m_buttons[i];

		std::vector<int>::iterator id =
				std::find(btn->ids.begin(),btn->ids.end(), eventID);

		if (id != btn->ids.end())
			return (touch_gui_button_id) i;
	}

	return after_last_element_id;
}

bool TouchScreenGUI::isHUDButton(const SEvent &event)
{
	// check if hud item is pressed
	for (std::map<int,rect<s32> >::iterator iter = m_hud_rects.begin();
			iter != m_hud_rects.end(); ++iter) {
		if (iter->second.isPointInside(
				v2s32(event.TouchInput.X,
						event.TouchInput.Y)
			)) {
			if ( iter->first < 8) {
				SEvent* translated = new SEvent();
				memset(translated,0,sizeof(SEvent));
				translated->EventType = irr::EET_KEY_INPUT_EVENT;
				translated->KeyInput.Key         = (irr::EKEY_CODE) (KEY_KEY_1 + iter->first);
				translated->KeyInput.Control     = false;
				translated->KeyInput.Shift       = false;
				translated->KeyInput.PressedDown = true;
				m_receiver->OnEvent(*translated);
				m_hud_ids[event.TouchInput.ID]   = translated->KeyInput.Key;
				delete translated;
				return true;
			}
		}
	}
	return false;
}

bool TouchScreenGUI::isReleaseHUDButton(int eventID)
{
	std::map<int,irr::EKEY_CODE>::iterator iter = m_hud_ids.find(eventID);

	if (iter != m_hud_ids.end()) {
		SEvent* translated = new SEvent();
		memset(translated,0,sizeof(SEvent));
		translated->EventType            = irr::EET_KEY_INPUT_EVENT;
		translated->KeyInput.Key         = iter->second;
		translated->KeyInput.PressedDown = false;
		translated->KeyInput.Control     = false;
		translated->KeyInput.Shift       = false;
		m_receiver->OnEvent(*translated);
		m_hud_ids.erase(iter);
		delete translated;
		return true;
	}
	return false;
}

void TouchScreenGUI::handleButtonEvent(touch_gui_button_id button,
		int eventID, bool action)
{
	button_info* btn = &m_buttons[button];
	SEvent* translated = new SEvent();
	memset(translated,0,sizeof(SEvent));
	translated->EventType            = irr::EET_KEY_INPUT_EVENT;
	translated->KeyInput.Key         = btn->keycode;
	translated->KeyInput.Control     = false;
	translated->KeyInput.Shift       = false;
	translated->KeyInput.Char        = 0;

	/* add this event */
	if (action) {
		assert(std::find(btn->ids.begin(),btn->ids.end(), eventID) == btn->ids.end());

		btn->ids.push_back(eventID);

		if (btn->ids.size() > 1) return;

		btn->repeatcounter = 0;
		translated->KeyInput.PressedDown = true;
		translated->KeyInput.Key = btn->keycode;
		m_receiver->OnEvent(*translated);
	}
	/* remove event */
	if ((!action) || (btn->immediate_release)) {

		std::vector<int>::iterator pos =
				std::find(btn->ids.begin(),btn->ids.end(), eventID);
		/* has to be in touch list */
		assert(pos != btn->ids.end());
		btn->ids.erase(pos);

		if (btn->ids.size() > 0)  { return; }

		translated->KeyInput.PressedDown = false;
		btn->repeatcounter               = -1;
		m_receiver->OnEvent(*translated);
	}
	delete translated;
}

void TouchScreenGUI::handleReleaseEvent(int evt_id)
{
	touch_gui_button_id button = getButtonID(evt_id);

	/* handle button events */
	if (button != after_last_element_id) {
		handleButtonEvent(button, evt_id, false);
	}
	/* handle hud button events */
	else if (isReleaseHUDButton(evt_id)) {
		/* nothing to do here */
	} else if (m_settingsbar.isReleaseButton(evt_id)) {
		/* nothing to do here */
	} else if (m_rarecontrolsbar.isReleaseButton(evt_id)) {
		/* nothing to do here */
	}
	/* handle the point used for moving view */
	else if (evt_id == m_move_id) {
		m_move_id = -1;

		/* if this pointer issued a mouse event issue symmetric release here */
		if (m_move_sent_as_mouse_event) {
			SEvent* translated = new SEvent;
			memset(translated,0,sizeof(SEvent));
			translated->EventType               = EET_MOUSE_INPUT_EVENT;
			translated->MouseInput.X            = m_move_downlocation.X;
			translated->MouseInput.Y            = m_move_downlocation.Y;
			translated->MouseInput.Shift        = false;
			translated->MouseInput.Control      = false;
			translated->MouseInput.ButtonStates = 0;
			translated->MouseInput.Event        = EMIE_LMOUSE_LEFT_UP;
			m_receiver->OnEvent(*translated);
			delete translated;
		}
		else {
			/* do double tap detection */
			doubleTapDetection();
		}
	}
	else {
		infostream
			<< "TouchScreenGUI::translateEvent released unknown button: "
			<< evt_id << std::endl;
	}

	for (std::vector<id_status>::iterator iter = m_known_ids.begin();
			iter != m_known_ids.end(); ++iter) {
		if (iter->id == evt_id) {
			m_known_ids.erase(iter);
			break;
		}
	}
}

void TouchScreenGUI::translateEvent(const SEvent &event)
{
	if (!m_visible) {
		infostream << "TouchScreenGUI::translateEvent got event but not visible?!" << std::endl;
		return;
	}

	if (event.EventType != EET_TOUCH_INPUT_EVENT) {
		return;
	}

	if (event.TouchInput.Event == ETIE_PRESSED_DOWN) {

		/* add to own copy of eventlist ...
		 * android would provide this information but irrlicht guys don't
		 * wanna design a efficient interface
		 */
		id_status toadd;
		toadd.id = event.TouchInput.ID;
		toadd.X  = event.TouchInput.X;
		toadd.Y  = event.TouchInput.Y;
		m_known_ids.push_back(toadd);

		int eventID = event.TouchInput.ID;

		touch_gui_button_id button =
				getButtonID(event.TouchInput.X, event.TouchInput.Y);

		/* handle button events */
		if (button != after_last_element_id) {
			handleButtonEvent(button, eventID, true);
			m_settingsbar.deactivate();
			m_rarecontrolsbar.deactivate();
		} else if (isHUDButton(event)) {
			m_settingsbar.deactivate();
			m_rarecontrolsbar.deactivate();
			/* already handled in isHUDButton() */
		} else if (m_settingsbar.isButton(event)) {
			m_rarecontrolsbar.deactivate();
			/* already handled in isSettingsBarButton() */
		} else if (m_rarecontrolsbar.isButton(event)) {
			m_settingsbar.deactivate();
			/* already handled in isSettingsBarButton() */
		}
		/* handle non button events */
		else {
			m_settingsbar.deactivate();
			m_rarecontrolsbar.deactivate();
			/* if we don't already have a moving point make this the moving one */
			if (m_move_id == -1) {
				m_move_id                  = event.TouchInput.ID;
				m_move_has_really_moved    = false;
				m_move_downtime            = porting::getTimeMs();
				m_move_downlocation        = v2s32(event.TouchInput.X, event.TouchInput.Y);
				m_move_sent_as_mouse_event = false;
			}
		}

		m_pointerpos[event.TouchInput.ID] = v2s32(event.TouchInput.X, event.TouchInput.Y);
	}
	else if (event.TouchInput.Event == ETIE_LEFT_UP) {
		verbosestream << "Up event for pointerid: " << event.TouchInput.ID << std::endl;
		handleReleaseEvent(event.TouchInput.ID);
	}
	else {
		assert(event.TouchInput.Event == ETIE_MOVED);
		int move_idx = event.TouchInput.ID;

		if (m_pointerpos[event.TouchInput.ID] ==
				v2s32(event.TouchInput.X, event.TouchInput.Y)) {
			return;
		}

		if (m_move_id != -1) {
			if ((event.TouchInput.ID == m_move_id) &&
				(!m_move_sent_as_mouse_event)) {

				double distance = sqrt(
						(m_pointerpos[event.TouchInput.ID].X - event.TouchInput.X) *
						(m_pointerpos[event.TouchInput.ID].X - event.TouchInput.X) +
						(m_pointerpos[event.TouchInput.ID].Y - event.TouchInput.Y) *
						(m_pointerpos[event.TouchInput.ID].Y - event.TouchInput.Y));

				if ((distance > g_settings->getU16("touchscreen_threshold")) ||
						(m_move_has_really_moved)) {
					m_move_has_really_moved = true;
					s32 X = event.TouchInput.X;
					s32 Y = event.TouchInput.Y;

					// update camera_yaw and camera_pitch
					s32 dx = X - m_pointerpos[event.TouchInput.ID].X;
					s32 dy = Y - m_pointerpos[event.TouchInput.ID].Y;

					/* adapt to similar behaviour as pc screen */
					double d         = g_settings->getFloat("mouse_sensitivity") *4;
					double old_yaw   = m_camera_yaw_change;
					double old_pitch = m_camera_pitch;

					m_camera_yaw_change -= dx * d;
					m_camera_pitch = MYMIN(MYMAX(m_camera_pitch + (dy * d), -180), 180);

					// update shootline
					m_shootline = m_device
							->getSceneManager()
							->getSceneCollisionManager()
							->getRayFromScreenCoordinates(v2s32(X, Y));
					m_pointerpos[event.TouchInput.ID] = v2s32(X, Y);
				}
			}
			else if ((event.TouchInput.ID == m_move_id) &&
					(m_move_sent_as_mouse_event)) {
				m_shootline = m_device
						->getSceneManager()
						->getSceneCollisionManager()
						->getRayFromScreenCoordinates(
								v2s32(event.TouchInput.X,event.TouchInput.Y));
			}
		} else {
			handleChangedButton(event);
		}
	}
}

void TouchScreenGUI::handleChangedButton(const SEvent &event)
{
	for (unsigned int i = 0; i < after_last_element_id; i++) {

		if (m_buttons[i].ids.empty()) {
			continue;
		}
		for (std::vector<int>::iterator iter = m_buttons[i].ids.begin();
				iter != m_buttons[i].ids.end(); ++iter) {

			if (event.TouchInput.ID == *iter) {

				int current_button_id =
						getButtonID(event.TouchInput.X, event.TouchInput.Y);

				if (current_button_id == i) {
					continue;
				}

				/* remove old button */
				handleButtonEvent((touch_gui_button_id) i,*iter,false);

				if (current_button_id == after_last_element_id) {
					return;
				}
				handleButtonEvent((touch_gui_button_id) current_button_id,*iter,true);
				return;

			}
		}
	}

	int current_button_id = getButtonID(event.TouchInput.X, event.TouchInput.Y);

	if (current_button_id == after_last_element_id) {
		return;
	}

	button_info* btn = &m_buttons[current_button_id];
	if (std::find(btn->ids.begin(),btn->ids.end(), event.TouchInput.ID)
			== btn->ids.end())
	{
		handleButtonEvent((touch_gui_button_id) current_button_id,
				event.TouchInput.ID, true);
	}

}

bool TouchScreenGUI::doubleTapDetection()
{
	m_key_events[0].down_time = m_key_events[1].down_time;
	m_key_events[0].x         = m_key_events[1].x;
	m_key_events[0].y         = m_key_events[1].y;
	m_key_events[1].down_time = m_move_downtime;
	m_key_events[1].x         = m_move_downlocation.X;
	m_key_events[1].y         = m_move_downlocation.Y;

	u64 delta = porting::getDeltaMs(m_key_events[0].down_time, porting::getTimeMs());
	if (delta > 400)
		return false;

	double distance = sqrt(
			(m_key_events[0].x - m_key_events[1].x) * (m_key_events[0].x - m_key_events[1].x) +
			(m_key_events[0].y - m_key_events[1].y) * (m_key_events[0].y - m_key_events[1].y));

	if (distance > (20 + g_settings->getU16("touchscreen_threshold")))
		return false;

	SEvent* translated = new SEvent();
	memset(translated, 0, sizeof(SEvent));
	translated->EventType               = EET_MOUSE_INPUT_EVENT;
	translated->MouseInput.X            = m_key_events[0].x;
	translated->MouseInput.Y            = m_key_events[0].y;
	translated->MouseInput.Shift        = false;
	translated->MouseInput.Control      = false;
	translated->MouseInput.ButtonStates = EMBSM_RIGHT;

	// update shootline
	m_shootline = m_device
			->getSceneManager()
			->getSceneCollisionManager()
			->getRayFromScreenCoordinates(v2s32(m_key_events[0].x, m_key_events[0].y));

	translated->MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
	verbosestream << "TouchScreenGUI::translateEvent right click press" << std::endl;
	m_receiver->OnEvent(*translated);

	translated->MouseInput.ButtonStates = 0;
	translated->MouseInput.Event        = EMIE_RMOUSE_LEFT_UP;
	verbosestream << "TouchScreenGUI::translateEvent right click release" << std::endl;
	m_receiver->OnEvent(*translated);
	delete translated;
	return true;

}

TouchScreenGUI::~TouchScreenGUI()
{
	for (unsigned int i = 0; i < after_last_element_id; i++) {
		button_info* btn = &m_buttons[i];
		if (btn->guibutton != 0) {
			btn->guibutton->drop();
			btn->guibutton = NULL;
		}
	}
}

void TouchScreenGUI::step(float dtime)
{
	/* simulate keyboard repeats */
	for (unsigned int i = 0; i < after_last_element_id; i++) {
		button_info* btn = &m_buttons[i];

		if (btn->ids.size() > 0) {
			btn->repeatcounter += dtime;

			/* in case we're moving around digging does not happen */
			if (m_move_id != -1)
				m_move_has_really_moved = true;

			if (btn->repeatcounter < btn->repeatdelay) continue;

			btn->repeatcounter              = 0;
			SEvent translated;
			memset(&translated, 0, sizeof(SEvent));
			translated.EventType            = irr::EET_KEY_INPUT_EVENT;
			translated.KeyInput.Key         = btn->keycode;
			translated.KeyInput.PressedDown = false;
			m_receiver->OnEvent(translated);

			translated.KeyInput.PressedDown = true;
			m_receiver->OnEvent(translated);
		}
	}

	/* if a new placed pointer isn't moved for some time start digging */
	if ((m_move_id != -1) &&
			(!m_move_has_really_moved) &&
			(!m_move_sent_as_mouse_event)) {

		u64 delta = porting::getDeltaMs(m_move_downtime, porting::getTimeMs());

		if (delta > MIN_DIG_TIME_MS) {
			m_shootline = m_device
					->getSceneManager()
					->getSceneCollisionManager()
					->getRayFromScreenCoordinates(
							v2s32(m_move_downlocation.X,m_move_downlocation.Y));

			SEvent translated;
			memset(&translated, 0, sizeof(SEvent));
			translated.EventType               = EET_MOUSE_INPUT_EVENT;
			translated.MouseInput.X            = m_move_downlocation.X;
			translated.MouseInput.Y            = m_move_downlocation.Y;
			translated.MouseInput.Shift        = false;
			translated.MouseInput.Control      = false;
			translated.MouseInput.ButtonStates = EMBSM_LEFT;
			translated.MouseInput.Event        = EMIE_LMOUSE_PRESSED_DOWN;
			verbosestream << "TouchScreenGUI::step left click press" << std::endl;
			m_receiver->OnEvent(translated);
			m_move_sent_as_mouse_event         = true;
		}
	}

	m_settingsbar.step(dtime);
	m_rarecontrolsbar.step(dtime);
}

void TouchScreenGUI::resetHud()
{
	m_hud_rects.clear();
}

void TouchScreenGUI::registerHudItem(int index, const rect<s32> &rect)
{
	m_hud_rects[index] = rect;
}

void TouchScreenGUI::Toggle(bool visible)
{
	m_visible = visible;
	for (unsigned int i = 0; i < after_last_element_id; i++) {
		button_info* btn = &m_buttons[i];
		if (btn->guibutton != 0) {
			btn->guibutton->setVisible(visible);
		}
	}

	/* clear all active buttons */
	if (!visible) {
		while (m_known_ids.size() > 0) {
			handleReleaseEvent(m_known_ids.begin()->id);
		}

		m_settingsbar.hide();
		m_rarecontrolsbar.hide();
	} else {
		m_settingsbar.show();
		m_rarecontrolsbar.show();
	}
}

void TouchScreenGUI::hide()
{
	if (!m_visible)
		return;

	Toggle(false);
}

void TouchScreenGUI::show()
{
	if (m_visible)
		return;

	Toggle(true);
}