diff --git a/ci/examples.ps1 b/ci/examples.ps1 index dc694907..29480394 100644 --- a/ci/examples.ps1 +++ b/ci/examples.ps1 @@ -1,9 +1,2 @@ echo "running example programs" .\install\bin\examples\colors.exe - -echo "testing read_stind" -if ((echo test | .\install\bin\examples\read_stdin.exe) -ne "Input from stdin: test") -{ - echo "read_stdin gave wrong output" - exit 1 -} diff --git a/ci/examples.sh b/ci/examples.sh index 32086c8f..89af39fa 100755 --- a/ci/examples.sh +++ b/ci/examples.sh @@ -3,13 +3,6 @@ set -ex ./install/bin/examples/colors -./install/bin/examples/read_stdin - -echo "testing stdin example" -if [[ $(echo test | ./install/bin/examples/read_stdin) != "Input from stdin: test" ]]; then - echo "stdin example returned wrong input" - exit 1 -fi echo "Expected to succeed:" ./install/bin/examples/colors < ./install/bin/examples/colors diff --git a/cpp-terminal/event.cpp b/cpp-terminal/event.cpp index ece18697..8446136a 100644 --- a/cpp-terminal/event.cpp +++ b/cpp-terminal/event.cpp @@ -1,5 +1,7 @@ #include "cpp-terminal/event.hpp" +#include "cpp-terminal/platforms/conversion.hpp" + bool Term::Event::empty() { if(m_Type == Type::Empty) return true; @@ -191,13 +193,19 @@ void Term::Event::parse() m_Key = Key(Term::Key::Value::F20); else if(m_str == "\033[G") m_Key = Key(Term::Key::Value::NUMERIC_5); - if(!m_Key.empty()) + else if(m_str.size() == 2 && ((m_str[0] & 0b11100000) == 0b11000000) && ((m_str[1] & 0b11000000) == 0b10000000)) { m_Key = Key(static_cast(Term::Private::utf8_to_utf32(m_str)[0])); } + else if(m_str.size() == 3 && ((m_str[0] & 0b11110000) == 0b11100000) && ((m_str[1] & 0b11000000) == 0b10000000) && ((m_str[2] & 0b11000000) == 0b10000000)) { m_Key = Key(static_cast(Term::Private::utf8_to_utf32(m_str)[0])); } + else if(m_str.size() == 4 && ((m_str[0] & 0b11111000) == 0b11110000) && ((m_str[1] & 0b11000000) == 0b10000000) && ((m_str[2] & 0b11000000) == 0b10000000) && ((m_str[2] & 0b11000000) == 0b10000000)) { - m_Type = Type::Key; - m_str.clear(); + m_Key = Key(static_cast(Term::Private::utf8_to_utf32(m_str)[0])); } } else { m_Type = Type::CopyPaste; } + if(!m_Key.empty()) + { + m_Type = Type::Key; + m_str.clear(); + } } Term::Event::operator Term::Key() diff --git a/cpp-terminal/input.cpp b/cpp-terminal/input.cpp index 4855a46a..2ae0892f 100644 --- a/cpp-terminal/input.cpp +++ b/cpp-terminal/input.cpp @@ -17,16 +17,3 @@ Term::Event Term::read_event() while((event = Platform::read_raw()).empty()) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } return event; } - -// returns the whole input from STDIN as string -std::string Term::read_stdin() -{ - std::string file; - char c{'\0'}; - while(true) - { - c = Platform::read_raw_stdin(); - if(c == 0x04) return file; - else { file.push_back(c); } - } -} diff --git a/cpp-terminal/input.hpp b/cpp-terminal/input.hpp index f11e8837..4f566802 100644 --- a/cpp-terminal/input.hpp +++ b/cpp-terminal/input.hpp @@ -12,8 +12,6 @@ namespace Platform // Returns true if a character is read, otherwise immediately returns false // This can't be made inline Term::Event read_raw(); - -char read_raw_stdin(); } // namespace Platform // Waits for an event (key press, screen resizing ...) diff --git a/cpp-terminal/key.cpp b/cpp-terminal/key.cpp index cbee45f3..38f2bbc7 100644 --- a/cpp-terminal/key.cpp +++ b/cpp-terminal/key.cpp @@ -1,5 +1,18 @@ #include "cpp-terminal/key.hpp" +#include "cpp-terminal/platforms/conversion.hpp" + +std::string Term::Key::str() +{ + std::string ret; + if(m_value >= 0x10FFFF) return std::string(); + else + { + Term::Private::codepoint_to_utf8(ret, static_cast(m_value)); + return ret; + } +} + Term::Key::Key(const Term::Key::Value& value) : m_value(value) {} Term::Key::operator Term::Key::Value() { return m_value; } diff --git a/cpp-terminal/key.hpp b/cpp-terminal/key.hpp index 500f28d1..2a56df49 100644 --- a/cpp-terminal/key.hpp +++ b/cpp-terminal/key.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace Term { @@ -223,7 +224,8 @@ class Key // Detect if Key is ALT+* bool isALT(); bool empty(); - char getChar(); + + std::string str(); private: Value m_value{NO_KEY}; diff --git a/cpp-terminal/platforms/input.cpp b/cpp-terminal/platforms/input.cpp index 635ebbb4..ee24e54c 100644 --- a/cpp-terminal/platforms/input.cpp +++ b/cpp-terminal/platforms/input.cpp @@ -1,6 +1,9 @@ #ifdef _WIN32 - #include + // clang-format off #include + #include + // clang-format on + #include #else #include #include @@ -25,20 +28,6 @@ static void sigwinchHandler(int sig) } #endif -char Term::Platform::read_raw_stdin() -{ - char c = getchar(); - if(c >= 0) { return c; } - else if(c == EOF) - { - // In non-raw (blocking) mode this happens when the input file - // ends. In such a case, return the End of Transmission (EOT) - // character (Ctrl-D) - return 0x04; - } - else { throw Term::Exception("getchar() failed"); } -} - Term::Event Term::Platform::read_raw() { #ifdef _WIN32 @@ -46,22 +35,24 @@ Term::Event Term::Platform::read_raw() GetNumberOfConsoleInputEvents(Private::std_cin.getHandler(), &nread); if(nread >= 1) { + std::string ret; + int processed{0}; DWORD nre{0}; std::vector buf{nread}; - if(!ReadConsoleInput(Private::std_cin.getHandler(), &buf[0], buf.size(), &nre)) { Term::Exception("ReadFile() failed"); } - std::string ret(nre, '\0'); - int processed{0}; + if(!ReadConsoleInputW(Private::std_cin.getHandler(), &buf[0], buf.size(), &nre)) { Term::Exception("ReadFile() failed"); } for(std::size_t i = 0; i != nre; ++i) { switch(buf[i].EventType) { case KEY_EVENT: { - WORD skip = buf[i].Event.KeyEvent.wVirtualKeyCode; //skip them for now - if(skip == VK_SHIFT || skip == VK_LWIN || skip == VK_RWIN || skip == VK_APPS || skip == VK_CONTROL || skip == VK_MENU || skip == VK_CAPITAL) break; + //if(buf[i].Event.KeyEvent.wVirtualKeyCode != 0) break; //skip them for now if(buf[i].Event.KeyEvent.bKeyDown) { - if(buf[i].Event.KeyEvent.uChar.AsciiChar != 0) ret[processed] = buf[i].Event.KeyEvent.uChar.AsciiChar; + std::size_t size_needed = WideCharToMultiByte(CP_UTF8, 0, &buf[i].Event.KeyEvent.uChar.UnicodeChar, -1, NULL, 0, NULL, NULL); + std::string strTo(size_needed, '\0'); + WideCharToMultiByte(CP_UTF8, 0, &buf[i].Event.KeyEvent.uChar.UnicodeChar, 1, &strTo[0], size_needed, NULL, NULL); + ret += strTo.c_str(); ++processed; break; /*else @@ -109,17 +100,26 @@ Term::Event Term::Platform::read_raw() break; } case FOCUS_EVENT: + { + break; + } case MENU_EVENT: + { + break; + } case MOUSE_EVENT: + { + break; + } case WINDOW_BUFFER_SIZE_EVENT: { - COORD coord{buf[i].Event.WindowBufferSizeEvent.dwSize}; - return Event(Screen(coord.Y, coord.X)); + return Event(Screen(buf[i].Event.WindowBufferSizeEvent.dwSize.Y, buf[i].Event.WindowBufferSizeEvent.dwSize.X)); } - default: continue; } } - return Event(ret.c_str()); + if(processed >= 1) return Event(ret); + else + return Event(); } else return Event(); diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index af16c0ae..823477f6 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -31,9 +31,9 @@ target_link_libraries(keys PRIVATE cpp-terminal::cpp-terminal Warnings::Warnings add_executable(colors colors.cpp) target_link_libraries(colors PRIVATE cpp-terminal::cpp-terminal Warnings::Warnings) -add_executable(read_stdin read_stdin.cpp) -target_link_libraries(read_stdin PRIVATE cpp-terminal::cpp-terminal Warnings::Warnings) +add_executable(events events.cpp) +target_link_libraries(events PRIVATE cpp-terminal::cpp-terminal Warnings::Warnings) if(CPPTERMINAL_ENABLE_INSTALL) - install(TARGETS cin minimal prompt_multiline prompt_immediate prompt_not_immediate prompt_simple kilo menu menu_window keys colors read_stdin RUNTIME DESTINATION bin/examples) + install(TARGETS cin minimal prompt_multiline prompt_immediate prompt_not_immediate prompt_simple kilo menu menu_window keys colors RUNTIME DESTINATION bin/examples) endif() diff --git a/examples/events.cpp b/examples/events.cpp new file mode 100644 index 00000000..efee5985 --- /dev/null +++ b/examples/events.cpp @@ -0,0 +1,62 @@ +#include +#include +#include +#include + +int main() +{ + try + { + // set options for the console + Term::terminal.setOptions(Term::Option::NoClearScreen, Term::Option::NoSignalKeys, Term::Option::Cursor, Term::Option::Raw); + // initial render of the whole screen + Term::terminal << "CTRL + Q to end" << std::endl; + bool main_loop_continue = true; + while(main_loop_continue) + { + auto event = Term::read_event(); + switch(event.type()) + { + case Term::Event::Type::Empty: + { + Term::terminal << "Event: Empty" << std::endl; + break; + } + case Term::Event::Type::Key: + { + Term::Key keyEvent = event; + if(keyEvent.iscntrl()) Term::terminal << "Event: Key (control key)" << std::endl; + else + Term::terminal << "Event: Key (" << keyEvent.str() << ")" << std::endl; + if(keyEvent == Term::Key::CTRL_Q) main_loop_continue = false; + break; + } + case Term::Event::Type::Screen: + { + Term::terminal << "Event: Screen" << std::endl; + break; + } + case Term::Event::Type::Cursor: + { + Term::terminal << "Event: Cursor" << std::endl; + break; + } + case Term::Event::Type::CopyPaste: + { + Term::terminal << "Event: CopyPaste (" << static_cast(event) << ")" << std::endl; + break; + } + default: + { + Term::terminal << "Event: Unknown" << std::endl; + break; + } + } + } + } + catch(...) + { + Term::terminal << "There was an exception!" << std::endl; + } + return 0; +} diff --git a/examples/read_stdin.cpp b/examples/read_stdin.cpp deleted file mode 100644 index a966f24e..00000000 --- a/examples/read_stdin.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "cpp-terminal/input.hpp" -#include "cpp-terminal/io.hpp" -#include "cpp-terminal/terminal.hpp" - -#include - -int main() -{ - Term::terminal.setOptions(Term::Option::NoClearScreen, Term::Option::NoSignalKeys, Term::Option::Cursor, Term::Option::Raw); - std::cout << "Input from stdin: " << Term::read_stdin() << std::endl; -}