diff --git a/.gitignore b/.gitignore index cbb57e35f8..edb9b19705 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ ext/SGCT # generated glsl files *.gglsl *.OpenSpaceGenerated.glsl +shaders/generated/* # CMake stuff CMakeCache.txt diff --git a/include/openspace/engine/openspaceengine.h b/include/openspace/engine/openspaceengine.h index d5b8e699ba..0a852455e7 100644 --- a/include/openspace/engine/openspaceengine.h +++ b/include/openspace/engine/openspaceengine.h @@ -34,6 +34,7 @@ #include #include #include +#include //#include #include @@ -92,6 +93,8 @@ public: void encode(); void decode(); + void setInputCommand(bool b); + private: OpenSpaceEngine(std::string programName); ~OpenSpaceEngine(); @@ -117,14 +120,7 @@ private: sgct::SharedVector _synchronizationBuffer; bool _inputCommand; - size_t _inputPosition; - std::vector _commandsHistory; - size_t _activeCommand; - std::vector _commands; - - void renderActiveCommand(); - void handleCommandInput(int key, int action); - void addToCommand(std::string c); + LuaConsole* _console; }; #define OsEng (openspace::OpenSpaceEngine::ref()) diff --git a/include/openspace/interaction/interactionhandler.h b/include/openspace/interaction/interactionhandler.h index f27a20604c..711240916b 100644 --- a/include/openspace/interaction/interactionhandler.h +++ b/include/openspace/interaction/interactionhandler.h @@ -1,3 +1,27 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014 * + * * + * 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 INTERACTIONHANDLER_H #define INTERACTIONHANDLER_H diff --git a/include/openspace/interaction/luaconsole.h b/include/openspace/interaction/luaconsole.h new file mode 100644 index 0000000000..ee6087f53c --- /dev/null +++ b/include/openspace/interaction/luaconsole.h @@ -0,0 +1,60 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014 * + * * + * 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 LUACONSOLE_H +#define LUACONSOLE_H + +#include +#include + +namespace openspace { + +class LuaConsole { +public: + LuaConsole(); + ~LuaConsole(); + + void keyboardCallback(int key, int action); + void charCallback(unsigned int codepoint); + + void render(); + + unsigned int commandInputButton(); + unsigned int ignoreCodepoint(); + + +private: + void addToCommand(std::string c); + std::string UnicodeToUTF8(unsigned int codepoint); + + size_t _inputPosition; + std::vector _commandsHistory; + size_t _activeCommand; + std::vector _commands; + +}; + +} // namespace openspace + +#endif diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index eb68261a24..bdfd3b116f 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -57,136 +58,10 @@ namespace { const std::string _sgctDefaultConfigFile = "${SGCT}/single.xml"; const std::string _sgctConfigArgumentCommand = "-config"; - -#ifdef WIN32 - const unsigned int CommandInputButton = SGCT_KEY_BACKSLASH; // Button left of 1 and abobe TAB - const unsigned int IgnoreCodepoint = 167; // Correesponding codepoint -#else - const unsigned int CommandInputButton = SGCT_KEY_GRAVE_ACCENT; // Button left of 1 and abobe TAB - const unsigned int IgnoreCodepoint = 167; // Correesponding codepoint - - // Dangerus as fuck - bool exec(const std::string& cmd, std::string& value) - { - FILE* pipe = popen(cmd.c_str(), "r"); - if (!pipe) - return false; - - const int buffer_size = 1024; - char buffer[buffer_size]; - value = ""; - while(!feof(pipe)) - { - if(fgets(buffer, buffer_size, pipe) != NULL) - { - value += buffer; - } - } - pclose(pipe); - return true; - } -#endif struct { std::string configurationName; } commandlineArgumentPlaceholders; - - // TODO: Put this functio nsomewhere appropriate - // get text from clipboard - std::string getClipboardText() - { -#ifdef WIN32 - // Try opening the clipboard - if (!OpenClipboard(nullptr)) - return ""; - - // Get handle of clipboard object for ANSI text - HANDLE hData = GetClipboardData(CF_TEXT); - if (hData == nullptr) - return ""; - - // Lock the handle to get the actual text pointer - char * pszText = static_cast(GlobalLock(hData)); - if (pszText == nullptr) - return ""; - - // Save text in a string class instance - std::string text(pszText); - - // Release the lock - GlobalUnlock(hData); - - // Release the clipboard - CloseClipboard(); - - text.erase(std::remove(text.begin(), text.end(), '\r'), text.end()); - return text; -#else - std::string text; - if(exec("xclip -o -sel c -f", text)) - return text.substr(0, text.length()-1); - return ""; // remove a line ending - -#endif - } - - // TODO: Put this function somewhere appropriate - // set text to clipboard - bool setClipboardText(std::string text) - { -#ifdef WIN32 - char *ptrData = nullptr; - HANDLE hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, text.length() + 1); - - ptrData = (char*)GlobalLock(hData); - memcpy(ptrData, text.c_str(), text.length() + 1); - - GlobalUnlock(hData); - - if (!OpenClipboard(nullptr)) - return false; - - if (!EmptyClipboard()) - return false; - - SetClipboardData(CF_TEXT, hData); - - CloseClipboard(); - - return true; -#else - std::stringstream cmd; - cmd << "echo \"" << text << "\" | xclip -i -sel c -f"; - std::string buf; - return exec(cmd.str(), buf); -#endif - } - - std::string UnicodeToUTF8(unsigned int codepoint){ - std::string out; - - if (codepoint <= 0x7f) - out.append(1, static_cast(codepoint)); - else if (codepoint <= 0x7ff) - { - out.append(1, static_cast(0xc0 | ((codepoint >> 6) & 0x1f))); - out.append(1, static_cast(0x80 | (codepoint & 0x3f))); - } - else if (codepoint <= 0xffff) - { - out.append(1, static_cast(0xe0 | ((codepoint >> 12) & 0x0f))); - out.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3f))); - out.append(1, static_cast(0x80 | (codepoint & 0x3f))); - } - else - { - out.append(1, static_cast(0xf0 | ((codepoint >> 18) & 0x07))); - out.append(1, static_cast(0x80 | ((codepoint >> 12) & 0x3f))); - out.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3f))); - out.append(1, static_cast(0x80 | (codepoint & 0x3f))); - } - return out; - } } using namespace ghoul::cmdparser; @@ -198,13 +73,14 @@ OpenSpaceEngine* OpenSpaceEngine::_engine = nullptr; OpenSpaceEngine::OpenSpaceEngine(std::string programName) : _commandlineParser(programName, true) , _inputCommand(false) - , _inputPosition(0) - , _activeCommand(0) - , _commands({""}) + , _console(nullptr) { } OpenSpaceEngine::~OpenSpaceEngine() { + if (_console) + delete _console; + SpiceManager::deinitialize(); Time::deinitialize(); DeviceIdentifier::deinit(); @@ -331,6 +207,8 @@ bool OpenSpaceEngine::create(int argc, char** argv, // Create the cachemanager FileSys.createCacheManager("${CACHE}"); + _engine->_console = new LuaConsole(); + // Determining SGCT configuration file LDEBUG("Determining SGCT configuration file"); std::string sgctConfigurationPath = _sgctDefaultConfigFile; @@ -541,48 +419,10 @@ void OpenSpaceEngine::render() { // If currently writing a command, render it to screen sgct::SGCTWindow* w = sgct::Engine::instance()->getActiveWindowPtr(); if (sgct::Engine::instance()->isMaster() && !w->isUsingFisheyeRendering() && _inputCommand) { - renderActiveCommand(); + _console->render(); } } -void OpenSpaceEngine::renderActiveCommand() { - - const int font_size = 10; - int x1, xSize, y1, ySize; - sgct::Engine::instance()->getActiveWindowPtr()->getCurrentViewportPixelCoords(x1, y1, xSize, ySize); - int startY = ySize - 2 * font_size; - startY = startY - font_size * 10 * 2; - - const int font_with = font_size*0.7; - const glm::vec4 red(1, 0, 0, 1); - const glm::vec4 green(0, 1, 0, 1); - const glm::vec4 white(1, 1, 1, 1); - const sgct_text::Font* font = sgct_text::FontManager::instance()->getFont(constants::fonts::keyMono, font_size); - Freetype::print(font, 10, startY, red, "$"); - Freetype::print(font, 10 + font_size, startY, white, "%s", _commands.at(_activeCommand).c_str()); - - size_t n = std::count(_commands.at(_activeCommand).begin(), _commands.at(_activeCommand).begin() + _inputPosition, '\n'); - size_t p = _commands.at(_activeCommand).find_last_of('\n', _inputPosition); - size_t linepos = _inputPosition; - - if (n>0) { - if (p == _inputPosition) { - p = _commands.at(_activeCommand).find_last_of('\n', _inputPosition - 1); - if (p != std::string::npos) { - linepos -= p + 1; - } - else { - linepos = _inputPosition - 1; - } - } - else{ - linepos -= p + 1; - } - } - char buffer[10]; - sprintf(buffer, "%%%lus", linepos + 1); - Freetype::print(font, 10 + static_cast(font_size)*0.5, startY - (font_size)*(n + 1)*3.0 / 2.0, green, buffer, "^"); -} void OpenSpaceEngine::postDraw() { if (sgct::Engine::instance()->isMaster()) @@ -606,16 +446,9 @@ void OpenSpaceEngine::postDraw() { #endif } -void OpenSpaceEngine::addToCommand(std::string c) { - size_t length = c.length(); - _commands.at(_activeCommand).insert(_inputPosition, c); - _inputPosition += length; -} - void OpenSpaceEngine::keyboardCallback(int key, int action) { if (sgct::Engine::instance()->isMaster()) { - - if (key == CommandInputButton && (action == SGCT_PRESS || action == SGCT_REPEAT)) { + if (key == _console->commandInputButton() && (action == SGCT_PRESS || action == SGCT_REPEAT)) { _inputCommand = !_inputCommand; } @@ -623,132 +456,14 @@ void OpenSpaceEngine::keyboardCallback(int key, int action) { _interactionHandler.keyboardCallback(key, action); } else { - handleCommandInput(key, action); - } - } -} - -void OpenSpaceEngine::handleCommandInput(int key, int action) { - if (action == SGCT_PRESS || action == SGCT_REPEAT) { - const size_t windowIndex = sgct::Engine::instance()->getFocusedWindowIndex(); - const bool mod_CONTROL = sgct::Engine::instance()->getKey(windowIndex, SGCT_KEY_LEFT_CONTROL) || - sgct::Engine::instance()->getKey(windowIndex, SGCT_KEY_RIGHT_CONTROL); - const bool mod_SHIFT = sgct::Engine::instance()->getKey(windowIndex, SGCT_KEY_LEFT_SHIFT) || - sgct::Engine::instance()->getKey(windowIndex, SGCT_KEY_RIGHT_SHIFT); - - // Paste from clipboard - if (key == SGCT_KEY_V) { - if (mod_CONTROL) { - addToCommand(getClipboardText()); - } - } - - // Copy to clipboard - if (key == SGCT_KEY_C) { - if (mod_CONTROL) { - setClipboardText(_commands.at(_activeCommand)); - } - } - - // Go to the previous character - if (key == SGCT_KEY_LEFT) { - if (_inputPosition > 0) - _inputPosition -= 1; - } - - // Go to the next character - if (key == SGCT_KEY_RIGHT) { - if (_inputPosition < _commands.at(_activeCommand).length()) - ++_inputPosition; - } - - // Go to previous command - if (key == SGCT_KEY_UP) { - if (_activeCommand > 0) - --_activeCommand; - _inputPosition = _commands.at(_activeCommand).length(); - } - - // Go to next command (the last is empty) - if (key == SGCT_KEY_DOWN) { - if (_activeCommand < _commands.size()-1) - ++_activeCommand; - _inputPosition = _commands.at(_activeCommand).length(); - } - - // Remove character before _inputPosition - if (key == SGCT_KEY_BACKSPACE) { - if (_inputPosition > 0) { - _commands.at(_activeCommand).erase(_inputPosition - 1, 1); - --_inputPosition; - } - } - - // Remove character after _inputPosition - if (key == SGCT_KEY_DELETE) { - if (_inputPosition <= _commands.at(_activeCommand).size()) { - _commands.at(_activeCommand).erase(_inputPosition, 1); - } - } - - // Go to the beginning of command string - if (key == SGCT_KEY_HOME) { - _inputPosition = 0; - } - - // Go to the end of command string - if (key == SGCT_KEY_END) { - _inputPosition = _commands.at(_activeCommand).size(); - } - - if (key == SGCT_KEY_ENTER) { - - // SHIFT+ENTER == new line - if (mod_SHIFT) { - addToCommand("\n"); - } - // CTRL+ENTER == Debug print the command - else if (mod_CONTROL) { - LDEBUG("Active command from next line:\n" << _commands.at(_activeCommand)); - } - // ENTER == run lua script - else { - if (_commands.at(_activeCommand) != "") { - - _scriptEngine.runScript(_commands.at(_activeCommand)); - _commandsHistory.push_back(_commands.at(_activeCommand)); - _commands = _commandsHistory; - _commands.push_back(""); - _activeCommand = _commands.size() - 1; - _inputPosition = 0; - } - else { - _commands = _commandsHistory; - _commands.push_back(""); - _inputCommand = false; - } - } + _console->keyboardCallback(key, action); } } } void OpenSpaceEngine::charCallback(unsigned int codepoint) { - - // SGCT_KEY_BACKSLASH == 92 but that corresponds to codepoint 167 - if (_inputCommand && codepoint != IgnoreCodepoint) { - -#ifndef WIN32 - const size_t windowIndex = sgct::Engine::instance()->getFocusedWindowIndex(); - const bool mod_CONTROL = sgct::Engine::instance()->getKey(windowIndex, SGCT_KEY_LEFT_CONTROL) || - sgct::Engine::instance()->getKey(windowIndex, SGCT_KEY_RIGHT_CONTROL); - - const int codepoint_C = 99; - const int codepoint_V = 118; - if(mod_CONTROL && (codepoint == codepoint_C || codepoint == codepoint_V)) { - return; - } -#endif - addToCommand(UnicodeToUTF8(codepoint)); + if (_inputCommand) { + _console->charCallback(codepoint); } } @@ -795,6 +510,10 @@ void OpenSpaceEngine::decode() //#endif } +void OpenSpaceEngine::setInputCommand(bool b) { + _inputCommand = b; +} + void OpenSpaceEngine::externalControlCallback(const char* receivedChars, int size, int clientId) { diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index 8da7b42048..6d08d3762b 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -1,3 +1,26 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014 * + * * + * 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. * + ****************************************************************************************/ // open space includes #include diff --git a/src/interaction/luaconsole.cpp b/src/interaction/luaconsole.cpp new file mode 100644 index 0000000000..9fed2973bd --- /dev/null +++ b/src/interaction/luaconsole.cpp @@ -0,0 +1,391 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014 * + * * + * 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 +#include +#include + +#include +#include + +#include + +#include + +namespace { + const std::string _loggerCat = "LuaConsole"; + const std::string _filename = "${TEMPORARY}/history"; + +#if !defined(WIN32) + // Dangerus as fuck (if malicious input) + bool exec(const std::string& cmd, std::string& value) + { + FILE* pipe = popen(cmd.c_str(), "r"); + if (!pipe) + return false; + + const int buffer_size = 1024; + char buffer[buffer_size]; + value = ""; + while (!feof(pipe)) + { + if (fgets(buffer, buffer_size, pipe) != NULL) + { + value += buffer; + } + } + pclose(pipe); + return true; + } +#endif + + // TODO: Put this functio nsomewhere appropriate + // get text from clipboard + std::string getClipboardText() { +#if defined(WIN32) + // Try opening the clipboard + if (!OpenClipboard(nullptr)) + return ""; + + // Get handle of clipboard object for ANSI text + HANDLE hData = GetClipboardData(CF_TEXT); + if (hData == nullptr) + return ""; + + // Lock the handle to get the actual text pointer + char * pszText = static_cast(GlobalLock(hData)); + if (pszText == nullptr) + return ""; + + // Save text in a string class instance + std::string text(pszText); + + // Release the lock + GlobalUnlock(hData); + + // Release the clipboard + CloseClipboard(); + + text.erase(std::remove(text.begin(), text.end(), '\r'), text.end()); + return text; +#elif defined(__APPLE__) + std::string text; + if (exec("pbpaste", text)) + return text.substr(0, text.length() - 1); + return ""; // remove a line ending +#else + std::string text; + if (exec("xclip -o -sel c -f", text)) + return text.substr(0, text.length() - 1); + return ""; // remove a line ending +#endif +} + + // TODO: Put this function somewhere appropriate + // set text to clipboard + bool setClipboardText(std::string text) + { +#if defined(WIN32) + char *ptrData = nullptr; + HANDLE hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, text.length() + 1); + + ptrData = (char*)GlobalLock(hData); + memcpy(ptrData, text.c_str(), text.length() + 1); + + GlobalUnlock(hData); + + if (!OpenClipboard(nullptr)) + return false; + + if (!EmptyClipboard()) + return false; + + SetClipboardData(CF_TEXT, hData); + + CloseClipboard(); + + return true; +#elif defined(__APPLE__) + std::stringstream cmd; + cmd << "echo \"" << text << "\" | pbcopy"; + std::string buf; + return exec(cmd.str(), buf); +#else + std::stringstream cmd; + cmd << "echo \"" << text << "\" | xclip -i -sel c -f"; + std::string buf; + return exec(cmd.str(), buf); +#endif + } +} + +namespace openspace { + +LuaConsole::LuaConsole() + : _inputPosition(0) + , _activeCommand(0) +{ + FILE* file = fopen(absPath(_filename).c_str(), "rb"); + if (file) { + size_t n; + fread(&n, sizeof(size_t), 1, file); + for (size_t i = 0; i < n; ++i) { + size_t length; + fread(&length, sizeof(size_t), 1, file); + char* tmp = new char[length + 1]; + fread(tmp, sizeof(char), length, file); + tmp[length] = '\0'; + _commandsHistory.emplace_back(tmp); + delete[] tmp; + } + fclose(file); + _commands = _commandsHistory; + } + _commands.push_back(""); + _activeCommand = _commands.size() - 1; +} + +LuaConsole::~LuaConsole() { + FILE* file = fopen(absPath(_filename).c_str(), "wb"); + if (file) { + size_t n = _commandsHistory.size(); + fwrite(&n, sizeof(size_t), 1, file); + for (auto s : _commandsHistory) { + size_t length = s.length(); + fwrite(&length, sizeof(size_t), 1, file); + fwrite(s.c_str(), sizeof(char), length, file); + } + fclose(file); + } +} + +void LuaConsole::keyboardCallback(int key, int action) { + if (action == SGCT_PRESS || action == SGCT_REPEAT) { + const size_t windowIndex = sgct::Engine::instance()->getFocusedWindowIndex(); + const bool mod_CONTROL = sgct::Engine::instance()->getKey(windowIndex, SGCT_KEY_LEFT_CONTROL) || + sgct::Engine::instance()->getKey(windowIndex, SGCT_KEY_RIGHT_CONTROL); + const bool mod_SHIFT = sgct::Engine::instance()->getKey(windowIndex, SGCT_KEY_LEFT_SHIFT) || + sgct::Engine::instance()->getKey(windowIndex, SGCT_KEY_RIGHT_SHIFT); + + // Paste from clipboard + if (key == SGCT_KEY_V) { + if (mod_CONTROL) { + addToCommand(getClipboardText()); + } + } + + // Copy to clipboard + if (key == SGCT_KEY_C) { + if (mod_CONTROL) { + setClipboardText(_commands.at(_activeCommand)); + } + } + + // Go to the previous character + if (key == SGCT_KEY_LEFT) { + if (_inputPosition > 0) + _inputPosition -= 1; + } + + // Go to the next character + if (key == SGCT_KEY_RIGHT) { + if (_inputPosition < _commands.at(_activeCommand).length()) + ++_inputPosition; + } + + // Go to previous command + if (key == SGCT_KEY_UP) { + if (_activeCommand > 0) + --_activeCommand; + _inputPosition = _commands.at(_activeCommand).length(); + } + + // Go to next command (the last is empty) + if (key == SGCT_KEY_DOWN) { + if (_activeCommand < _commands.size() - 1) + ++_activeCommand; + _inputPosition = _commands.at(_activeCommand).length(); + } + + // Remove character before _inputPosition + if (key == SGCT_KEY_BACKSPACE) { + if (_inputPosition > 0) { + _commands.at(_activeCommand).erase(_inputPosition - 1, 1); + --_inputPosition; + } + } + + // Remove character after _inputPosition + if (key == SGCT_KEY_DELETE) { + if (_inputPosition <= _commands.at(_activeCommand).size()) { + _commands.at(_activeCommand).erase(_inputPosition, 1); + } + } + + // Go to the beginning of command string + if (key == SGCT_KEY_HOME) { + _inputPosition = 0; + } + + // Go to the end of command string + if (key == SGCT_KEY_END) { + _inputPosition = _commands.at(_activeCommand).size(); + } + + if (key == SGCT_KEY_ENTER) { + + // SHIFT+ENTER == new line + if (mod_SHIFT) { + addToCommand("\n"); + } + // CTRL+ENTER == Debug print the command + else if (mod_CONTROL) { + LDEBUG("Active command from next line:\n" << _commands.at(_activeCommand)); + } + // ENTER == run lua script + else { + if (_commands.at(_activeCommand) != "") { + + OsEng.scriptEngine().runScript(_commands.at(_activeCommand)); + _commandsHistory.push_back(_commands.at(_activeCommand)); + _commands = _commandsHistory; + _commands.push_back(""); + _activeCommand = _commands.size() - 1; + _inputPosition = 0; + } + else { + _commands = _commandsHistory; + _commands.push_back(""); + OsEng.setInputCommand(false); + } + } + } + } +} + +void LuaConsole::charCallback(unsigned int codepoint) { + if (codepoint == ignoreCodepoint()) + return; + +#ifndef WIN32 + const size_t windowIndex = sgct::Engine::instance()->getFocusedWindowIndex(); + const bool mod_CONTROL = sgct::Engine::instance()->getKey(windowIndex, SGCT_KEY_LEFT_CONTROL) || + sgct::Engine::instance()->getKey(windowIndex, SGCT_KEY_RIGHT_CONTROL); + + const int codepoint_C = 99; + const int codepoint_V = 118; + if (mod_CONTROL && (codepoint == codepoint_C || codepoint == codepoint_V)) { + return; + } +#endif + addToCommand(UnicodeToUTF8(codepoint)); + +} + +void LuaConsole::render() { + const int font_size = 10; + int x1, xSize, y1, ySize; + sgct::Engine::instance()->getActiveWindowPtr()->getCurrentViewportPixelCoords(x1, y1, xSize, ySize); + int startY = ySize - 2 * font_size; + startY = startY - font_size * 10 * 2; + + const int font_with = font_size*0.7; + const glm::vec4 red(1, 0, 0, 1); + const glm::vec4 green(0, 1, 0, 1); + const glm::vec4 white(1, 1, 1, 1); + const sgct_text::Font* font = sgct_text::FontManager::instance()->getFont(constants::fonts::keyMono, font_size); + Freetype::print(font, 10, startY, red, "$"); + Freetype::print(font, 10 + font_size, startY, white, "%s", _commands.at(_activeCommand).c_str()); + + size_t n = std::count(_commands.at(_activeCommand).begin(), _commands.at(_activeCommand).begin() + _inputPosition, '\n'); + size_t p = _commands.at(_activeCommand).find_last_of('\n', _inputPosition); + size_t linepos = _inputPosition; + + if (n>0) { + if (p == _inputPosition) { + p = _commands.at(_activeCommand).find_last_of('\n', _inputPosition - 1); + if (p != std::string::npos) { + linepos -= p + 1; + } + else { + linepos = _inputPosition - 1; + } + } + else{ + linepos -= p + 1; + } + } + char buffer[10]; + sprintf(buffer, "%%%lus", linepos + 1); + Freetype::print(font, 10 + static_cast(font_size)*0.5, startY - (font_size)*(n + 1)*3.0 / 2.0, green, buffer, "^"); +} + +unsigned int LuaConsole::commandInputButton(){ + // Button left of 1 and abobe TAB +#ifdef WIN32 + return SGCT_KEY_BACKSLASH; +#else + return SGCT_KEY_GRAVE_ACCENT; +#endif +} + +unsigned int LuaConsole::ignoreCodepoint() { + // Correesponding codepoint for commandInputButton() + return 167; +} + +void LuaConsole::addToCommand(std::string c) { + size_t length = c.length(); + _commands.at(_activeCommand).insert(_inputPosition, c); + _inputPosition += length; +} + +std::string LuaConsole::UnicodeToUTF8(unsigned int codepoint) { + std::string out; + + if (codepoint <= 0x7f) + out.append(1, static_cast(codepoint)); + else if (codepoint <= 0x7ff) + { + out.append(1, static_cast(0xc0 | ((codepoint >> 6) & 0x1f))); + out.append(1, static_cast(0x80 | (codepoint & 0x3f))); + } + else if (codepoint <= 0xffff) + { + out.append(1, static_cast(0xe0 | ((codepoint >> 12) & 0x0f))); + out.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3f))); + out.append(1, static_cast(0x80 | (codepoint & 0x3f))); + } + else + { + out.append(1, static_cast(0xf0 | ((codepoint >> 18) & 0x07))); + out.append(1, static_cast(0x80 | ((codepoint >> 12) & 0x3f))); + out.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3f))); + out.append(1, static_cast(0x80 | (codepoint & 0x3f))); + } + return out; +} + + +} // namespace openspace