mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2025-12-16 01:48:56 +08:00
First version of supporting extraction of the terminal id.
This commit is contained in:
@@ -144,6 +144,7 @@ add_library(component
|
||||
src/ftxui/component/resizable_split.cpp
|
||||
src/ftxui/component/screen_interactive.cpp
|
||||
src/ftxui/component/slider.cpp
|
||||
src/ftxui/component/terminal_id.cpp
|
||||
src/ftxui/component/terminal_input_parser.cpp
|
||||
src/ftxui/component/terminal_input_parser.hpp
|
||||
src/ftxui/component/util.cpp
|
||||
|
||||
@@ -46,6 +46,7 @@ example(slider_direction)
|
||||
example(slider_rgb)
|
||||
example(tab_horizontal)
|
||||
example(tab_vertical)
|
||||
example(terminal_id)
|
||||
example(textarea)
|
||||
example(toggle)
|
||||
example(window)
|
||||
|
||||
33
examples/component/terminal_id.cpp
Normal file
33
examples/component/terminal_id.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2025 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
// the LICENSE file.
|
||||
|
||||
#include "ftxui/component/component.hpp" // for Renderer
|
||||
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
|
||||
#include "ftxui/dom/elements.hpp" // for text, hbox, separator, Element, operator|, vbox, border
|
||||
|
||||
int main() {
|
||||
using namespace ftxui;
|
||||
|
||||
std::string terminal_id = "UNKNOWN";
|
||||
|
||||
auto screen =
|
||||
ScreenInteractive::TerminalOutput();
|
||||
|
||||
screen.OnTerminalIDUpdate([&terminal_id] (
|
||||
TerminalID const& terminal_id_update)
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << terminal_id_update;
|
||||
terminal_id = stream.str();
|
||||
});
|
||||
|
||||
auto renderer = Renderer([&]
|
||||
{
|
||||
return vbox({
|
||||
text("Terminal id " + terminal_id),
|
||||
}) | border;
|
||||
});
|
||||
|
||||
screen.Loop(renderer);
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
#define FTXUI_COMPONENT_EVENT_HPP
|
||||
|
||||
#include <ftxui/component/mouse.hpp> // for Mouse
|
||||
#include <ftxui/component/terminal_id.hpp> // for TerminalID
|
||||
#include <string> // for string, operator==
|
||||
|
||||
namespace ftxui {
|
||||
@@ -35,6 +36,7 @@ struct Event {
|
||||
static Event Mouse(std::string, Mouse mouse);
|
||||
static Event CursorPosition(std::string, int x, int y); // Internal
|
||||
static Event CursorShape(std::string, int shape); // Internal
|
||||
static Event TerminalID(std::string input, enum TerminalID terminal_id); // Internal
|
||||
|
||||
// --- Arrow ---
|
||||
static const Event ArrowLeft;
|
||||
@@ -117,6 +119,9 @@ struct Event {
|
||||
bool is_cursor_shape() const { return type_ == Type::CursorShape; }
|
||||
int cursor_shape() const { return data_.cursor_shape; }
|
||||
|
||||
bool is_terminal_id() const { return type_ == Type::TerminalID; }
|
||||
enum TerminalID terminal_id() const { return data_.terminal_id; }
|
||||
|
||||
// Debug
|
||||
std::string DebugString() const;
|
||||
|
||||
@@ -132,6 +137,7 @@ struct Event {
|
||||
Mouse,
|
||||
CursorPosition,
|
||||
CursorShape,
|
||||
TerminalID
|
||||
};
|
||||
Type type_ = Type::Unknown;
|
||||
|
||||
@@ -144,6 +150,7 @@ struct Event {
|
||||
struct Mouse mouse;
|
||||
struct Cursor cursor;
|
||||
int cursor_shape;
|
||||
enum TerminalID terminal_id;
|
||||
} data_ = {};
|
||||
|
||||
std::string input_;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <memory> // for shared_ptr
|
||||
#include <string> // for string
|
||||
#include <thread> // for thread
|
||||
#include <list>
|
||||
|
||||
#include "ftxui/component/animation.hpp" // for TimePoint
|
||||
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
|
||||
@@ -72,6 +73,13 @@ class ScreenInteractive : public Screen {
|
||||
void ForceHandleCtrlC(bool force);
|
||||
void ForceHandleCtrlZ(bool force);
|
||||
|
||||
TerminalID TerminalId() const;
|
||||
|
||||
typedef std::function<void(TerminalID const& terminal_id)> TerminalIDUpdateCallback;
|
||||
|
||||
void OnTerminalIDUpdate(
|
||||
TerminalIDUpdateCallback const& callback);
|
||||
|
||||
// Selection API.
|
||||
std::string GetSelection();
|
||||
void SelectionChange(std::function<void()> callback);
|
||||
@@ -156,6 +164,11 @@ class ScreenInteractive : public Screen {
|
||||
std::unique_ptr<Selection> selection_;
|
||||
std::function<void()> selection_on_change_;
|
||||
|
||||
TerminalID m_terminal_id;
|
||||
|
||||
typedef std::list<TerminalIDUpdateCallback> TerminalIDUpdateCallbackContainer;
|
||||
TerminalIDUpdateCallbackContainer m_terminal_id_update_callbacks;
|
||||
|
||||
friend class Loop;
|
||||
|
||||
public:
|
||||
|
||||
35
include/ftxui/component/terminal_id.hpp
Normal file
35
include/ftxui/component/terminal_id.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2025 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
// the LICENSE file.
|
||||
|
||||
#ifndef FTXUI_COMPONENT_TERMINAL_ID_HPP
|
||||
#define FTXUI_COMPONENT_TERMINAL_ID_HPP
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
std::string const TERMINAL_ID_REQUEST("\x1b[0c");
|
||||
|
||||
/// @brief A mouse event. It contains the coordinate of the mouse, the button
|
||||
/// pressed and the modifier (shift, ctrl, meta).
|
||||
/// @ingroup component
|
||||
|
||||
enum class TerminalID
|
||||
{
|
||||
UNKNOWN,
|
||||
XTERM,
|
||||
KONSOLE,
|
||||
URXVT,
|
||||
VTE,
|
||||
LINUXVC
|
||||
};
|
||||
|
||||
std::ostream& operator<<(
|
||||
std::ostream& os,
|
||||
TerminalID terminal_id);
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
#endif /* end of include guard: FTXUI_COMPONENT_TERMINAL_ID_HPP */
|
||||
@@ -87,6 +87,16 @@ Event Event::CursorPosition(std::string input, int x, int y) {
|
||||
return event;
|
||||
}
|
||||
|
||||
/// @internal
|
||||
// static
|
||||
Event Event::TerminalID(std::string input, enum TerminalID terminal_id) {
|
||||
Event event;
|
||||
event.input_ = std::move(input);
|
||||
event.type_ = Type::TerminalID;
|
||||
event.data_.terminal_id = terminal_id;
|
||||
return event;
|
||||
}
|
||||
|
||||
/// @brief Return a string representation of the event.
|
||||
std::string Event::DebugString() const {
|
||||
static std::map<Event, const char*> event_to_string = {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright 2022 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
// the LICENSE file.
|
||||
#include <iostream>
|
||||
#include "ftxui/component/loop.hpp"
|
||||
#include "ftxui/component/terminal_id.hpp"
|
||||
|
||||
#include <utility> // for move
|
||||
|
||||
@@ -47,6 +49,9 @@ void Loop::RunOnceBlocking() {
|
||||
/// Execute the loop, blocking the current thread, up until the loop has
|
||||
/// quitted.
|
||||
void Loop::Run() {
|
||||
// Ensure we perform a single terminal id request before we are starting the loop itself.
|
||||
std::cout << TERMINAL_ID_REQUEST;
|
||||
|
||||
while (!HasQuitted()) {
|
||||
RunOnceBlocking();
|
||||
}
|
||||
|
||||
@@ -352,7 +352,8 @@ ScreenInteractive::ScreenInteractive(Dimension dimension,
|
||||
bool use_alternative_screen)
|
||||
: Screen(dimx, dimy),
|
||||
dimension_(dimension),
|
||||
use_alternative_screen_(use_alternative_screen) {
|
||||
use_alternative_screen_(use_alternative_screen),
|
||||
m_terminal_id(TerminalID::UNKNOWN) {
|
||||
task_receiver_ = MakeReceiver<Task>();
|
||||
}
|
||||
|
||||
@@ -799,6 +800,29 @@ void ScreenInteractive::HandleTask(Component component, Task& task) {
|
||||
arg.mouse().y -= cursor_y_;
|
||||
}
|
||||
|
||||
if (arg.is_terminal_id())
|
||||
{
|
||||
m_terminal_id =
|
||||
arg.terminal_id();
|
||||
|
||||
for (auto itr = m_terminal_id_update_callbacks.begin(),
|
||||
end_itr = m_terminal_id_update_callbacks.end();
|
||||
(itr != end_itr);
|
||||
++itr)
|
||||
{
|
||||
if (*itr)
|
||||
{
|
||||
(*itr)(m_terminal_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The callback function is invalid and will be removed
|
||||
m_terminal_id_update_callbacks.erase(
|
||||
itr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arg.screen_ = this;
|
||||
|
||||
bool handled = component->OnEvent(arg);
|
||||
@@ -1084,4 +1108,16 @@ bool ScreenInteractive::SelectionData::operator!=(
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
TerminalID ScreenInteractive::TerminalId() const
|
||||
{
|
||||
return m_terminal_id;
|
||||
}
|
||||
|
||||
void ScreenInteractive::OnTerminalIDUpdate(
|
||||
TerminalIDUpdateCallback const& callback)
|
||||
{
|
||||
m_terminal_id_update_callbacks.push_back(
|
||||
callback);
|
||||
}
|
||||
|
||||
} // namespace ftxui.
|
||||
|
||||
51
src/ftxui/component/terminal_id.cpp
Normal file
51
src/ftxui/component/terminal_id.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright 2025 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
// the LICENSE file.
|
||||
|
||||
#include "ftxui/component/terminal_id.hpp"
|
||||
|
||||
namespace ftxui
|
||||
{
|
||||
|
||||
std::ostream& operator<<(
|
||||
std::ostream& os,
|
||||
TerminalID terminal_id)
|
||||
{
|
||||
switch(terminal_id)
|
||||
{
|
||||
case TerminalID::UNKNOWN:
|
||||
{
|
||||
os << "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
case TerminalID::XTERM:
|
||||
{
|
||||
os << "XTERM";
|
||||
break;
|
||||
}
|
||||
case TerminalID::KONSOLE:
|
||||
{
|
||||
os << "KONSOLE";
|
||||
break;
|
||||
}
|
||||
case TerminalID::URXVT:
|
||||
{
|
||||
os << "URXVT";
|
||||
break;
|
||||
}
|
||||
case TerminalID::VTE:
|
||||
{
|
||||
os << "VTE";
|
||||
break;
|
||||
}
|
||||
case TerminalID::LINUXVC:
|
||||
{
|
||||
os << "LINUXVC";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
@@ -161,6 +161,11 @@ void TerminalInputParser::Send(TerminalInputParser::Output output) {
|
||||
out_->Send(Event::CursorShape(std::move(pending_), output.cursor_shape));
|
||||
pending_.clear();
|
||||
return;
|
||||
|
||||
case TERMINAL_ID:
|
||||
out_->Send(Event::TerminalID(std::move(pending_), output.terminal_id));
|
||||
pending_.clear();
|
||||
return;
|
||||
}
|
||||
// NOT_REACHED().
|
||||
}
|
||||
@@ -374,6 +379,8 @@ TerminalInputParser::Output TerminalInputParser::ParseCSI() {
|
||||
return ParseMouse(altered, false, std::move(arguments));
|
||||
case 'R':
|
||||
return ParseCursorPosition(std::move(arguments));
|
||||
case 'c':
|
||||
return ParseTerminalID(std::move(arguments));
|
||||
default:
|
||||
return SPECIAL;
|
||||
}
|
||||
@@ -461,4 +468,53 @@ TerminalInputParser::Output TerminalInputParser::ParseCursorPosition(
|
||||
return output;
|
||||
}
|
||||
|
||||
TerminalInputParser::Output TerminalInputParser::ParseTerminalID(
|
||||
std::vector<int> arguments)
|
||||
{
|
||||
Output output(TERMINAL_ID);
|
||||
|
||||
if (!arguments.empty())
|
||||
{
|
||||
switch(arguments[0])
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
output.terminal_id =
|
||||
TerminalID::URXVT;
|
||||
|
||||
break;
|
||||
}
|
||||
case 6:
|
||||
{
|
||||
output.terminal_id =
|
||||
TerminalID::LINUXVC;
|
||||
|
||||
break;
|
||||
}
|
||||
case 62:
|
||||
{
|
||||
output.terminal_id =
|
||||
TerminalID::KONSOLE;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
output.terminal_id =
|
||||
TerminalID::UNKNOWN;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
output.terminal_id =
|
||||
TerminalID::UNKNOWN;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse
|
||||
#include "ftxui/component/terminal_id.hpp" // for TerminalId
|
||||
#include "ftxui/component/receiver.hpp" // for Sender
|
||||
#include "ftxui/component/task.hpp" // for Task
|
||||
|
||||
@@ -33,6 +34,7 @@ class TerminalInputParser {
|
||||
CURSOR_POSITION,
|
||||
CURSOR_SHAPE,
|
||||
SPECIAL,
|
||||
TERMINAL_ID
|
||||
};
|
||||
|
||||
struct CursorPosition {
|
||||
@@ -46,6 +48,7 @@ class TerminalInputParser {
|
||||
Mouse mouse;
|
||||
CursorPosition cursor{};
|
||||
int cursor_shape;
|
||||
TerminalID terminal_id;
|
||||
};
|
||||
|
||||
Output(Type t) // NOLINT
|
||||
@@ -61,6 +64,7 @@ class TerminalInputParser {
|
||||
Output ParseOSC();
|
||||
Output ParseMouse(bool altered, bool pressed, std::vector<int> arguments);
|
||||
Output ParseCursorPosition(std::vector<int> arguments);
|
||||
Output ParseTerminalID(std::vector<int> arguments);
|
||||
|
||||
Sender<Task> out_;
|
||||
int position_ = -1;
|
||||
|
||||
@@ -510,5 +510,28 @@ TEST(Event, DeviceControlString) {
|
||||
EXPECT_FALSE(event_receiver->Receive(&received));
|
||||
}
|
||||
|
||||
TEST(Event, TerminalID) {
|
||||
|
||||
// Test terminal id for KDE konsole
|
||||
auto event_receiver = MakeReceiver<Task>();
|
||||
{
|
||||
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
||||
parser.Add('\x1B');
|
||||
parser.Add('[');
|
||||
parser.Add('?');
|
||||
parser.Add('6');
|
||||
parser.Add('2');
|
||||
parser.Add(';');
|
||||
parser.Add('2');
|
||||
parser.Add('c');
|
||||
}
|
||||
|
||||
Task received;
|
||||
EXPECT_TRUE(event_receiver->Receive(&received));
|
||||
EXPECT_TRUE(std::get<Event>(received).is_terminal_id());
|
||||
EXPECT_EQ(TerminalID::KONSOLE, std::get<Event>(received).terminal_id());
|
||||
EXPECT_FALSE(event_receiver->Receive(&received));
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
// NOLINTEND
|
||||
|
||||
Reference in New Issue
Block a user