mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2025-05-06 08:06:58 +08:00
Add Windows input.
Co-authored-by: Mikael Olenfalk <mikael@olenfalk.se> Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
This commit is contained in:
parent
ff59c7bafa
commit
5fc5766f1e
@ -114,6 +114,11 @@ foreach(lib screen dom component)
|
|||||||
target_compile_options(${lib} PRIVATE "-Werror")
|
target_compile_options(${lib} PRIVATE "-Werror")
|
||||||
target_compile_options(${lib} PRIVATE "-Wno-sign-compare")
|
target_compile_options(${lib} PRIVATE "-Wno-sign-compare")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Force Win32 to UNICODE
|
||||||
|
if(MSVC)
|
||||||
|
target_compile_definitions(${lib} PRIVATE UNICODE _UNICODE)
|
||||||
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
if(FTXUI_ENABLE_INSTALL)
|
if(FTXUI_ENABLE_INSTALL)
|
||||||
|
55
include/ftxui/component/event_input_listener.hpp
Normal file
55
include/ftxui/component/event_input_listener.hpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#ifndef FTXUI_COMPONENT_EVENT_INPUT_LISTENER_HPP
|
||||||
|
#define FTXUI_COMPONENT_EVENT_INPUT_LISTENER_HPP
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "event.hpp"
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <deque>
|
||||||
|
#include <mutex>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ftxui {
|
||||||
|
|
||||||
|
// Receives input events from the OS and turns them into
|
||||||
|
// Event objects and sends to a consumer
|
||||||
|
//
|
||||||
|
// On NIX systems:
|
||||||
|
// - uses SIGWINCH for resize
|
||||||
|
// - uses getchar() for keypresses
|
||||||
|
//
|
||||||
|
// On Windows systems:
|
||||||
|
// - Uses ReadConsoleInput for resize and keypresses
|
||||||
|
class EventInputListener {
|
||||||
|
public:
|
||||||
|
EventInputListener(std::function<void(Event)> consumer);
|
||||||
|
~EventInputListener();
|
||||||
|
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
char readchar();
|
||||||
|
void readchar_thread_func(std::function<void(Event)> consumer);
|
||||||
|
|
||||||
|
std::atomic<bool> quit_{false};
|
||||||
|
std::thread readchar_thread_;
|
||||||
|
#ifndef _WIN32
|
||||||
|
using signal_handler_t = void (*)(int);
|
||||||
|
signal_handler_t old_sigwinch_handler_;
|
||||||
|
#else
|
||||||
|
void input_thread_func(std::function<void(Event)> consumer);
|
||||||
|
|
||||||
|
std::mutex input_queue_mutex_;
|
||||||
|
std::condition_variable input_queue_condvar_;
|
||||||
|
std::deque<char> input_queue_;
|
||||||
|
std::thread input_event_thread_;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ftxui
|
||||||
|
|
||||||
|
#endif /* end of include guard: FTXUI_COMPONENT_EVENT_INPUT_LISTENER_HPP */
|
@ -9,13 +9,17 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "ftxui/component/component.hpp"
|
#include "ftxui/component/component.hpp"
|
||||||
|
#include "ftxui/component/event_input_listener.hpp"
|
||||||
#include "ftxui/screen/string.hpp"
|
#include "ftxui/screen/string.hpp"
|
||||||
#include "ftxui/screen/terminal.hpp"
|
#include "ftxui/screen/terminal.hpp"
|
||||||
|
|
||||||
#ifdef WIN32
|
#if defined(WIN32)
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
#ifndef UNICODE
|
||||||
|
#error Must be compiled in UNICODE mode
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -35,6 +39,56 @@ void CharToEventStream(Receiver<char> receiver, Sender<Event> sender) {
|
|||||||
Event::Convert(receiver, sender, c);
|
Event::Convert(receiver, sender, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(WIN32)
|
||||||
|
|
||||||
|
void Win32EventListener(std::atomic<bool>* quit,
|
||||||
|
Sender<char> char_sender,
|
||||||
|
Sender<Event> event_sender) {
|
||||||
|
auto console = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
|
while (!*quit) {
|
||||||
|
// Throttle ReadConsoleInput by waiting 250ms, this wait function will
|
||||||
|
// return if there is input in the console.
|
||||||
|
auto wait_result = WaitForSingleObject(console, 250);
|
||||||
|
if (wait_result == WAIT_TIMEOUT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DWORD number_of_events = 0;
|
||||||
|
if (!GetNumberOfConsoleInputEvents(console, &number_of_events))
|
||||||
|
continue;
|
||||||
|
if (number_of_events <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::vector<INPUT_RECORD> records{number_of_events};
|
||||||
|
DWORD number_of_events_read = 0;
|
||||||
|
ReadConsoleInput(console, records.data(),
|
||||||
|
(DWORD)(records.size() * sizeof(INPUT_RECORD)),
|
||||||
|
&number_of_events_read);
|
||||||
|
records.resize(number_of_events_read);
|
||||||
|
|
||||||
|
for (const auto& r : records) {
|
||||||
|
switch (r.EventType) {
|
||||||
|
case KEY_EVENT: {
|
||||||
|
auto key_event = r.Event.KeyEvent;
|
||||||
|
// ignore UP key events
|
||||||
|
if (key_event.bKeyDown == FALSE)
|
||||||
|
continue;
|
||||||
|
char_sender->Send((char)key_event.uChar.UnicodeChar);
|
||||||
|
} break;
|
||||||
|
case WINDOW_BUFFER_SIZE_EVENT:
|
||||||
|
event_sender->Send(Event::Special({0}));
|
||||||
|
break;
|
||||||
|
case MENU_EVENT:
|
||||||
|
case FOCUS_EVENT:
|
||||||
|
case MOUSE_EVENT:
|
||||||
|
// TODO(mauve): Implement later.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
// Read char from the terminal.
|
// Read char from the terminal.
|
||||||
void UnixEventListener(std::atomic<bool>* quit, Sender<char> sender) {
|
void UnixEventListener(std::atomic<bool>* quit, Sender<char> sender) {
|
||||||
// TODO(arthursonzogni): Use a timeout so that it doesn't block even if the
|
// TODO(arthursonzogni): Use a timeout so that it doesn't block even if the
|
||||||
@ -43,6 +97,8 @@ void UnixEventListener(std::atomic<bool>* quit, Sender<char> sender) {
|
|||||||
sender->Send((char)getchar());
|
sender->Send((char)getchar());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
static const char* HIDE_CURSOR = "\x1B[?25l";
|
static const char* HIDE_CURSOR = "\x1B[?25l";
|
||||||
static const char* SHOW_CURSOR = "\x1B[?25h";
|
static const char* SHOW_CURSOR = "\x1B[?25h";
|
||||||
|
|
||||||
@ -105,11 +161,8 @@ void ScreenInteractive::PostEvent(Event event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ScreenInteractive::Loop(Component* component) {
|
void ScreenInteractive::Loop(Component* component) {
|
||||||
// Install a SIGINT handler and restore the old handler on exit.
|
|
||||||
install_signal_handler(SIGINT, OnExit);
|
|
||||||
|
|
||||||
// Save the old terminal configuration and restore it on exit.
|
// Save the old terminal configuration and restore it on exit.
|
||||||
#ifdef WIN32
|
#if defined(WIN32)
|
||||||
// Enable VT processing on stdout and stdin
|
// Enable VT processing on stdout and stdin
|
||||||
auto stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
auto stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
auto stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
|
auto stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
@ -141,7 +194,7 @@ void ScreenInteractive::Loop(Component* component) {
|
|||||||
tcsetattr(STDIN_FILENO, TCSANOW, &terminal);
|
tcsetattr(STDIN_FILENO, TCSANOW, &terminal);
|
||||||
|
|
||||||
// Handle resize.
|
// Handle resize.
|
||||||
on_resize = [&] { PostEvent(Event::Special({0})); };
|
on_resize = [&] { event_sender_->Send(Event::Special({0})); };
|
||||||
install_signal_handler(SIGWINCH, OnResize);
|
install_signal_handler(SIGWINCH, OnResize);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -159,15 +212,18 @@ void ScreenInteractive::Loop(Component* component) {
|
|||||||
// Produce a stream of Event from a stream of char.
|
// Produce a stream of Event from a stream of char.
|
||||||
auto char_receiver = MakeReceiver<char>();
|
auto char_receiver = MakeReceiver<char>();
|
||||||
auto char_sender = char_receiver->MakeSender();
|
auto char_sender = char_receiver->MakeSender();
|
||||||
auto event_sender = event_receiver_->MakeSender();
|
auto event_sender_1 = event_receiver_->MakeSender();
|
||||||
auto char_to_event_stream = std::thread(
|
auto char_to_event_stream = std::thread(
|
||||||
CharToEventStream, std::move(char_receiver), std::move(event_sender));
|
CharToEventStream, std::move(char_receiver), std::move(event_sender_1));
|
||||||
|
|
||||||
// Depending on the OS, start a thread that will produce events and/or chars.
|
// Depending on the OS, start a thread that will produce events and/or chars.
|
||||||
#if defined(WIN32)
|
#if defined(WIN32)
|
||||||
// TODO(arthursonzogni) implement here.
|
auto event_sender_2 = event_receiver_->MakeSender();
|
||||||
|
auto event_listener =
|
||||||
|
std::thread(&Win32EventListener, &quit_, std::move(char_sender),
|
||||||
|
std::move(event_sender_2));
|
||||||
#else
|
#else
|
||||||
auto unix_event_listener =
|
auto event_listener =
|
||||||
std::thread(&UnixEventListener, &quit_, std::move(char_sender));
|
std::thread(&UnixEventListener, &quit_, std::move(char_sender));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -183,9 +239,7 @@ void ScreenInteractive::Loop(Component* component) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char_to_event_stream.join();
|
char_to_event_stream.join();
|
||||||
#if !defined(WIN32)
|
event_listener.join();
|
||||||
unix_event_listener.join();
|
|
||||||
#endif
|
|
||||||
OnExit(0);
|
OnExit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#include "ftxui/screen/string.hpp"
|
#include "ftxui/screen/string.hpp"
|
||||||
#include "ftxui/screen/terminal.hpp"
|
#include "ftxui/screen/terminal.hpp"
|
||||||
|
|
||||||
#ifdef WIN32
|
#if defined(WIN32)
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#ifdef WIN32
|
#if defined(WIN32)
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
Loading…
Reference in New Issue
Block a user