aboutsummaryrefslogtreecommitdiff
path: root/builtin/init.lua
Commit message (Collapse)AuthorAge
* Mainmenu: Refactor tab UI codeRui9142016-04-08
| | | | | | - Use local variables for tabs in place of globals - Merge together if statements where possible - Replace manual table searching code with indexof where possible
* builtin: Fix `print` crashing on nil "holes".Diego Martinez2016-02-08
| | | | | | | | | | | | | The engine implementation of `print` packs the varargs into a table and passes the table directly to `table.concat`. If you pass any value not supported by `table.concat` (particularly `nil`), the server crashes. This is unexpected behavior, as `print` is supposed to be able to work with anything. This patch changes the implementation so it first converts all arguments using `tostring`, which fixes the issue and makes the custom `print` function compatible with the stock Lua behavior.
* Add server side ncurses terminalest312015-11-06
| | | | | | | | | | | | | | | | | | | | | | | | This adds a chat console the server owner can use for administration or to talk with players. It runs in its own thread, which makes the user interface immune to the server's lag, behaving just like a client, except timeout. As it uses the same console code as the f10 console, things like nick completion or a scroll buffer basically come for free. The terminal itself is written in a general way so that adding a client version later on is just about implementing an interface. Fatal errors are printed after the console exists and the ncurses terminal buffer gets cleaned up with endwin(), so that the error still remains visible. The server owner can chose their username their entered text will have in chat and where players can send PMs to. Once the username is secured with a password to prevent anybody to take over the server, the owner can execute admin tasks over the console. This change includes a contribution by @kahrl who has improved ncurses library detection.
* Refactor loggingShadowNinja2015-10-14
| | | | | | | | | - Add warning log level - Change debug_log_level setting to enumeration string - Map Irrlicht log events to MT log events - Encapsulate log_* functions and global variables into a class, Logger - Unify dstream with standard logging mechanism - Unify core.debug() with standard core.log() script API
* Add strict moduleShadowNinja2014-11-19
| | | | Also fix leaking globals found by it.
* Fix regression main_menu_script setting not working any longersapier2014-06-14
|
* Use "core" namespace internallyShadowNinja2014-05-08
|
* Organize builtin into subdirectoriesShadowNinja2014-05-07
'>229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
/*
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.
*/

#include "keycode.h"
#include "exceptions.h"
#include "settings.h"
#include "log.h"
#include "debug.h"
#include "util/hex.h"
#include "util/string.h"
#include "util/basic_macros.h"

class UnknownKeycode : public BaseException
{
public:
	UnknownKeycode(const char *s) :
		BaseException(s) {};
};

struct table_key {
	const char *Name;
	irr::EKEY_CODE Key;
	wchar_t Char; // L'\0' means no character assigned
	const char *LangName; // NULL means it doesn't have a human description
};

#define DEFINEKEY1(x, lang) /* Irrlicht key without character */ \
	{ #x, irr::x, L'\0', lang },
#define DEFINEKEY2(x, ch, lang) /* Irrlicht key with character */ \
	{ #x, irr::x, ch, lang },
#define DEFINEKEY3(ch) /* single Irrlicht key (e.g. KEY_KEY_X) */ \
	{ "KEY_KEY_" TOSTRING(ch), irr::KEY_KEY_ ## ch, (wchar_t) *TOSTRING(ch), TOSTRING(ch) },
#define DEFINEKEY4(ch) /* single Irrlicht function key (e.g. KEY_F3) */ \
	{ "KEY_F" TOSTRING(ch), irr::KEY_F ## ch, L'\0', "F" TOSTRING(ch) },
#define DEFINEKEY5(ch) /* key without Irrlicht keycode */ \
	{ ch, irr::KEY_KEY_CODES_COUNT, (wchar_t) *ch, ch },

#define N_(text) text

static const struct table_key table[] = {
	// Keys that can be reliably mapped between Char and Key
	DEFINEKEY3(0)
	DEFINEKEY3(1)
	DEFINEKEY3(2)
	DEFINEKEY3(3)
	DEFINEKEY3(4)
	DEFINEKEY3(5)
	DEFINEKEY3(6)
	DEFINEKEY3(7)
	DEFINEKEY3(8)
	DEFINEKEY3(9)
	DEFINEKEY3(A)
	DEFINEKEY3(B)
	DEFINEKEY3(C)
	DEFINEKEY3(D)
	DEFINEKEY3(E)
	DEFINEKEY3(F)
	DEFINEKEY3(G)
	DEFINEKEY3(H)
	DEFINEKEY3(I)
	DEFINEKEY3(J)
	DEFINEKEY3(K)
	DEFINEKEY3(L)
	DEFINEKEY3(M)
	DEFINEKEY3(N)
	DEFINEKEY3(O)
	DEFINEKEY3(P)
	DEFINEKEY3(Q)
	DEFINEKEY3(R)
	DEFINEKEY3(S)
	DEFINEKEY3(T)
	DEFINEKEY3(U)
	DEFINEKEY3(V)
	DEFINEKEY3(W)
	DEFINEKEY3(X)
	DEFINEKEY3(Y)
	DEFINEKEY3(Z)
	DEFINEKEY2(KEY_PLUS, L'+', "+")
	DEFINEKEY2(KEY_COMMA, L',', ",")
	DEFINEKEY2(KEY_MINUS, L'-', "-")
	DEFINEKEY2(KEY_PERIOD, L'.', ".")

	// Keys without a Char
	DEFINEKEY1(KEY_LBUTTON, N_("Left Button"))
	DEFINEKEY1(KEY_RBUTTON, N_("Right Button"))
	DEFINEKEY1(KEY_CANCEL, N_("Cancel"))
	DEFINEKEY1(KEY_MBUTTON, N_("Middle Button"))
	DEFINEKEY1(KEY_XBUTTON1, N_("X Button 1"))
	DEFINEKEY1(KEY_XBUTTON2, N_("X Button 2"))
	DEFINEKEY1(KEY_BACK, N_("Backspace"))
	DEFINEKEY1(KEY_TAB, N_("Tab"))
	DEFINEKEY1(KEY_CLEAR, N_("Clear"))
	DEFINEKEY1(KEY_RETURN, N_("Return"))
	DEFINEKEY1(KEY_SHIFT, N_("Shift"))
	DEFINEKEY1(KEY_CONTROL, N_("Control"))
	//~ Key name, common on Windows keyboards
	DEFINEKEY1(KEY_MENU, N_("Menu"))
	DEFINEKEY1(KEY_PAUSE, N_("Pause"))
	DEFINEKEY1(KEY_CAPITAL, N_("Caps Lock"))
	DEFINEKEY1(KEY_SPACE, N_("Space"))
	DEFINEKEY1(KEY_PRIOR, N_("Page up"))
	DEFINEKEY1(KEY_NEXT, N_("Page down"))
	DEFINEKEY1(KEY_END, N_("End"))
	DEFINEKEY1(KEY_HOME, N_("Home"))
	DEFINEKEY1(KEY_LEFT, N_("Left"))
	DEFINEKEY1(KEY_UP, N_("Up"))
	DEFINEKEY1(KEY_RIGHT, N_("Right"))
	DEFINEKEY1(KEY_DOWN, N_("Down"))
	//~ Key name
	DEFINEKEY1(KEY_SELECT, N_("Select"))
	//~ "Print screen" key
	DEFINEKEY1(KEY_PRINT, N_("Print"))
	DEFINEKEY1(KEY_EXECUT, N_("Execute"))
	DEFINEKEY1(KEY_SNAPSHOT, N_("Snapshot"))
	DEFINEKEY1(KEY_INSERT, N_("Insert"))
	DEFINEKEY1(KEY_DELETE, N_("Delete"))
	DEFINEKEY1(KEY_HELP, N_("Help"))
	DEFINEKEY1(KEY_LWIN, N_("Left Windows"))
	DEFINEKEY1(KEY_RWIN, N_("Right Windows"))
	DEFINEKEY1(KEY_NUMPAD0, N_("Numpad 0")) // These are not assigned to a char
	DEFINEKEY1(KEY_NUMPAD1, N_("Numpad 1")) // to prevent interference with KEY_KEY_[0-9].
	DEFINEKEY1(KEY_NUMPAD2, N_("Numpad 2"))
	DEFINEKEY1(KEY_NUMPAD3, N_("Numpad 3"))
	DEFINEKEY1(KEY_NUMPAD4, N_("Numpad 4"))
	DEFINEKEY1(KEY_NUMPAD5, N_("Numpad 5"))
	DEFINEKEY1(KEY_NUMPAD6, N_("Numpad 6"))
	DEFINEKEY1(KEY_NUMPAD7, N_("Numpad 7"))
	DEFINEKEY1(KEY_NUMPAD8, N_("Numpad 8"))
	DEFINEKEY1(KEY_NUMPAD9, N_("Numpad 9"))
	DEFINEKEY1(KEY_MULTIPLY, N_("Numpad *"))
	DEFINEKEY1(KEY_ADD, N_("Numpad +"))
	DEFINEKEY1(KEY_SEPARATOR, N_("Numpad ."))
	DEFINEKEY1(KEY_SUBTRACT, N_("Numpad -"))
	DEFINEKEY1(KEY_DECIMAL, NULL)
	DEFINEKEY1(KEY_DIVIDE, N_("Numpad /"))
	DEFINEKEY4(1)
	DEFINEKEY4(2)
	DEFINEKEY4(3)
	DEFINEKEY4(4)
	DEFINEKEY4(5)
	DEFINEKEY4(6)
	DEFINEKEY4(7)
	DEFINEKEY4(8)
	DEFINEKEY4(9)
	DEFINEKEY4(10)
	DEFINEKEY4(11)
	DEFINEKEY4(12)
	DEFINEKEY4(13)
	DEFINEKEY4(14)
	DEFINEKEY4(15)
	DEFINEKEY4(16)
	DEFINEKEY4(17)
	DEFINEKEY4(18)
	DEFINEKEY4(19)
	DEFINEKEY4(20)
	DEFINEKEY4(21)
	DEFINEKEY4(22)
	DEFINEKEY4(23)
	DEFINEKEY4(24)
	DEFINEKEY1(KEY_NUMLOCK, N_("Num Lock"))
	DEFINEKEY1(KEY_SCROLL, N_("Scroll Lock"))
	DEFINEKEY1(KEY_LSHIFT, N_("Left Shift"))
	DEFINEKEY1(KEY_RSHIFT, N_("Right Shift"))
	DEFINEKEY1(KEY_LCONTROL, N_("Left Control"))
	DEFINEKEY1(KEY_RCONTROL, N_("Right Control"))
	DEFINEKEY1(KEY_LMENU, N_("Left Menu"))
	DEFINEKEY1(KEY_RMENU, N_("Right Menu"))

	// Rare/weird keys
	DEFINEKEY1(KEY_KANA, "Kana")
	DEFINEKEY1(KEY_HANGUEL, "Hangul")
	DEFINEKEY1(KEY_HANGUL, "Hangul")
	DEFINEKEY1(KEY_JUNJA, "Junja")
	DEFINEKEY1(KEY_FINAL, "Final")
	DEFINEKEY1(KEY_KANJI, "Kanji")
	DEFINEKEY1(KEY_HANJA, "Hanja")
	DEFINEKEY1(KEY_ESCAPE, N_("IME Escape"))
	DEFINEKEY1(KEY_CONVERT, N_("IME Convert"))
	DEFINEKEY1(KEY_NONCONVERT, N_("IME Nonconvert"))
	DEFINEKEY1(KEY_ACCEPT, N_("IME Accept"))
	DEFINEKEY1(KEY_MODECHANGE, N_("IME Mode Change"))
	DEFINEKEY1(KEY_APPS, N_("Apps"))
	DEFINEKEY1(KEY_SLEEP, N_("Sleep"))
#if !(IRRLICHT_VERSION_MAJOR <= 1 && IRRLICHT_VERSION_MINOR <= 7 && IRRLICHT_VERSION_REVISION < 3)
	DEFINEKEY1(KEY_OEM_1, "OEM 1") // KEY_OEM_[0-9] and KEY_OEM_102 are assigned to multiple
	DEFINEKEY1(KEY_OEM_2, "OEM 2") // different chars (on different platforms too) and thus w/o char
	DEFINEKEY1(KEY_OEM_3, "OEM 3")
	DEFINEKEY1(KEY_OEM_4, "OEM 4")
	DEFINEKEY1(KEY_OEM_5, "OEM 5")
	DEFINEKEY1(KEY_OEM_6, "OEM 6")
	DEFINEKEY1(KEY_OEM_7, "OEM 7")
	DEFINEKEY1(KEY_OEM_8, "OEM 8")
	DEFINEKEY1(KEY_OEM_AX, "OEM AX")
	DEFINEKEY1(KEY_OEM_102, "OEM 102")
#endif
	DEFINEKEY1(KEY_ATTN, "Attn")
	DEFINEKEY1(KEY_CRSEL, "CrSel")
	DEFINEKEY1(KEY_EXSEL, "ExSel")
	DEFINEKEY1(KEY_EREOF, N_("Erase EOF"))
	DEFINEKEY1(KEY_PLAY, N_("Play"))
	DEFINEKEY1(KEY_ZOOM, N_("Zoom"))
	DEFINEKEY1(KEY_PA1, "PA1")
	DEFINEKEY1(KEY_OEM_CLEAR, N_("OEM Clear"))

	// Keys without Irrlicht keycode
	DEFINEKEY5("!")
	DEFINEKEY5("\"")
	DEFINEKEY5("#")
	DEFINEKEY5("$")
	DEFINEKEY5("%")
	DEFINEKEY5("&")
	DEFINEKEY5("'")
	DEFINEKEY5("(")
	DEFINEKEY5(")")
	DEFINEKEY5("*")
	DEFINEKEY5("/")
	DEFINEKEY5(":")
	DEFINEKEY5(";")
	DEFINEKEY5("<")
	DEFINEKEY5("=")
	DEFINEKEY5(">")
	DEFINEKEY5("?")
	DEFINEKEY5("@")
	DEFINEKEY5("[")
	DEFINEKEY5("\\")
	DEFINEKEY5("]")
	DEFINEKEY5("^")
	DEFINEKEY5("_")
};

#undef N_


struct table_key lookup_keyname(const char *name)
{
	for (const auto &table_key : table) {
		if (strcmp(table_key.Name, name) == 0)
			return table_key;
	}

	throw UnknownKeycode(name);
}

struct table_key lookup_keykey(irr::EKEY_CODE key)
{
	for (const auto &table_key : table) {
		if (table_key.Key == key)
			return table_key;
	}

	std::ostringstream os;
	os << "<Keycode " << (int) key << ">";
	throw UnknownKeycode(os.str().c_str());
}

struct table_key lookup_keychar(wchar_t Char)
{
	for (const auto &table_key : table) {
		if (table_key.Char == Char)
			return table_key;
	}

	std::ostringstream os;
	os << "<Char " << hex_encode((char*) &Char, sizeof(wchar_t)) << ">";
	throw UnknownKeycode(os.str().c_str());
}

KeyPress::KeyPress(const char *name)
{
	if (strlen(name) == 0) {
		Key = irr::KEY_KEY_CODES_COUNT;
		Char = L'\0';
		m_name = "";
		return;
	}

	if (strlen(name) <= 4) {
		// Lookup by resulting character
		int chars_read = mbtowc(&Char, name, 1);
		FATAL_ERROR_IF(chars_read != 1, "Unexpected multibyte character");
		try {
			struct table_key k = lookup_keychar(Char);
			m_name = k.Name;
			Key = k.Key;
			return;
		} catch (UnknownKeycode &e) {};
	} else {
		// Lookup by name
		m_name = name;
		try {
			struct table_key k = lookup_keyname(name);
			Key = k.Key;
			Char = k.Char;
			return;
		} catch (UnknownKeycode &e) {};
	}

	// It's not a known key, complain and try to do something
	Key = irr::KEY_KEY_CODES_COUNT;
	int chars_read = mbtowc(&Char, name, 1);
	FATAL_ERROR_IF(chars_read != 1, "Unexpected multibyte character");
	m_name = "";
	warningstream << "KeyPress: Unknown key '" << name << "', falling back to first char.";
}

KeyPress::KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character)
{
	if (prefer_character)
		Key = irr::KEY_KEY_CODES_COUNT;
	else
		Key = in.Key;
	Char = in.Char;

	try {
		if (valid_kcode(Key))
			m_name = lookup_keykey(Key).Name;
		else
			m_name = lookup_keychar(Char).Name;
	} catch (UnknownKeycode &e) {
		m_name = "";
	};
}

const char *KeyPress::sym() const
{
	return m_name.c_str();
}

const char *KeyPress::name() const
{
	if (m_name.empty())
		return "";
	const char *ret;
	if (valid_kcode(Key))
		ret = lookup_keykey(Key).LangName;
	else
		ret = lookup_keychar(Char).LangName;
	return ret ? ret : "<Unnamed key>";
}

const KeyPress EscapeKey("KEY_ESCAPE");
const KeyPress CancelKey("KEY_CANCEL");

/*
	Key config
*/

// A simple cache for quicker lookup
std::unordered_map<std::string, KeyPress> g_key_setting_cache;

KeyPress getKeySetting(const char *settingname)
{
	std::unordered_map<std::string, KeyPress>::iterator n;
	n = g_key_setting_cache.find(settingname);
	if (n != g_key_setting_cache.end())
		return n->second;

	KeyPress k(g_settings->get(settingname).c_str());
	g_key_setting_cache[settingname] = k;
	return k;
}

void clearKeyCache()
{
	g_key_setting_cache.clear();
}

irr::EKEY_CODE keyname_to_keycode(const char *name)
{
	return lookup_keyname(name).Key;
}