diff --git a/CMakeLists.txt b/CMakeLists.txt index eed335f77..224f626b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -223,6 +223,8 @@ endif() find_library(HAVE_LIBREADLINE NAMES ${PREFERRED_LIBREADLINE}) if(HAVE_LIBREADLINE) set(LIB_LIBREADLINE ${HAVE_LIBREADLINE}) +elseif(MSVC) + set(HAVE_LIBREADLINE 1) endif() # ===================================== diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 462d9eae1..c51214e6e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -70,6 +70,7 @@ if(MSVC) "msvc/getopt.c" "msvc/gettimeofday.c" "msvc/usleep.cpp" + "msvc/readline.cpp" ) set(EXTRA_WINDOWS_INCLUDES ${EXTRA_WINDOWS_INCLUDES} "msvc" diff --git a/src/msvc/readline.cpp b/src/msvc/readline.cpp new file mode 100644 index 000000000..4fed1b1e5 --- /dev/null +++ b/src/msvc/readline.cpp @@ -0,0 +1,95 @@ +// +// readline.cpp +// Copyright (C) 2022 Marius Greuel +// SPDX-License-Identifier: GPL-2.0-or-later +// + +#include +#include +#include +#include +#include +#include +#include "readline/readline.h" +#include "readline/history.h" + +int rl_readline_version = 0x0502; + +static rl_vcpfunc_t* rl_handler; +static std::unique_ptr rl_thread; +static std::mutex rl_mutex; +static std::string rl_line; +static bool rl_has_line = false; + +static void get_line_thread() +{ + std::string line; + std::getline(std::cin, line); + + const std::lock_guard lock(rl_mutex); + rl_line = line; + rl_has_line = true; +} + +static void call_handler(const char* string) +{ + if (rl_thread) + { + rl_thread->join(); + rl_thread = nullptr; + } + + if (rl_handler != nullptr) + { + if (string == nullptr) + { + rl_handler(nullptr); + } + else + { + rl_handler(_strdup(string)); + } + } +} + +int rl_input_available(void) +{ + return 1; +} + +void rl_callback_read_char(void) +{ + if (std::cin.eof()) + { + call_handler(nullptr); + } + else if (!rl_thread) + { + rl_thread = std::make_unique(get_line_thread); + } + else + { + const std::lock_guard lock(rl_mutex); + if (rl_has_line) + { + rl_has_line = false; + call_handler(rl_line.c_str()); + } + } +} + +void rl_callback_handler_install(char* prompt, rl_vcpfunc_t* handler) +{ + rl_handler = handler; + + std::cout << prompt; +} + +void rl_callback_handler_remove(void) +{ + rl_handler = nullptr; +} + +void add_history(const char*) +{ +} diff --git a/src/msvc/readline/history.h b/src/msvc/readline/history.h new file mode 100644 index 000000000..86a7b0a8c --- /dev/null +++ b/src/msvc/readline/history.h @@ -0,0 +1,17 @@ +// +// history.h +// Copyright (C) 2022 Marius Greuel +// SPDX-License-Identifier: GPL-2.0-or-later +// + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void add_history(const char* string); + +#ifdef __cplusplus +} +#endif diff --git a/src/msvc/readline/readline.h b/src/msvc/readline/readline.h new file mode 100644 index 000000000..6a7d7a555 --- /dev/null +++ b/src/msvc/readline/readline.h @@ -0,0 +1,24 @@ +// +// readline.h +// Copyright (C) 2022 Marius Greuel +// SPDX-License-Identifier: GPL-2.0-or-later +// + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (rl_vcpfunc_t)(char* line); + +extern int rl_readline_version; + +int rl_input_available(void); +void rl_callback_read_char(void); +void rl_callback_handler_install(char* prompt, rl_vcpfunc_t* handler); +void rl_callback_handler_remove(void); + +#ifdef __cplusplus +} +#endif diff --git a/src/term.c b/src/term.c index d53d2df38..0828e17e5 100644 --- a/src/term.c +++ b/src/term.c @@ -1314,7 +1314,9 @@ static int term_running; // Any character in standard input available (without sleeping)? static int readytoread() { -#ifdef WIN32 +#ifdef _MSC_VER + return rl_input_available(); +#elif defined(WIN32) HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); while(1) {