mirror of
https://github.com/genodelabs/genode.git
synced 2025-05-02 08:42:52 +00:00
parent
6399fc12ac
commit
fc7b983a40
@ -5,5 +5,6 @@ input_session
|
|||||||
nitpicker_gfx
|
nitpicker_gfx
|
||||||
terminal_session
|
terminal_session
|
||||||
timer_session
|
timer_session
|
||||||
|
report_session
|
||||||
vfs
|
vfs
|
||||||
gems
|
gems
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
This is a graphical terminal implementation. It provides the Terminal
|
This is a graphical terminal implementation. It provides the Terminal
|
||||||
service and uses a nitpicker session for screen representation.
|
service and uses a nitpicker session for screen representation.
|
||||||
|
|
||||||
Configuration
|
|
||||||
~~~~~~~~~~~~~
|
Color configuration
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The default color palette can be configured via the <palette> XML
|
The default color palette can be configured via the <palette> XML
|
||||||
configuration node like follows. There are 16 colors configurable -
|
configuration node like follows. There are 16 colors configurable -
|
||||||
index 0-7 normal color and index 8-15 bright (bold) colors.
|
index 0-7 normal color and index 8-15 bright (bold) colors.
|
||||||
|
|
||||||
|
|
||||||
! <config>
|
! <config>
|
||||||
! <palette>
|
! <palette>
|
||||||
! <color index="0" value="#000000"/> <!-- black is real black -->
|
! <color index="0" value="#000000"/> <!-- black is real black -->
|
||||||
@ -17,3 +17,17 @@ index 0-7 normal color and index 8-15 bright (bold) colors.
|
|||||||
! ...
|
! ...
|
||||||
! </config>
|
! </config>
|
||||||
|
|
||||||
|
|
||||||
|
Clipboard support
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
With the '<config>' attribute 'copy="yes"' specified, the terminal allows
|
||||||
|
the user to select text to be reported to a "clipboard" report. The selection
|
||||||
|
mode is activated by holding the left shift key. While the selection mode
|
||||||
|
is active, the text position under mouse pointer is highlighted and the
|
||||||
|
user can select text via the left mouse button. Upon release of the mouse
|
||||||
|
button, the selection is reported.
|
||||||
|
|
||||||
|
Vice versa, with the '<config>' attribute 'paste="yes"' specified, the
|
||||||
|
terminal allows the user to paste the content of a "clipboard" ROM session
|
||||||
|
to the terminal client by pressing the middle mouse button.
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <base/attached_rom_dataspace.h>
|
#include <base/attached_rom_dataspace.h>
|
||||||
#include <base/attached_ram_dataspace.h>
|
#include <base/attached_ram_dataspace.h>
|
||||||
#include <input/event.h>
|
#include <input/event.h>
|
||||||
|
#include <os/reporter.h>
|
||||||
#include <gems/vfs.h>
|
#include <gems/vfs.h>
|
||||||
#include <gems/vfs_font.h>
|
#include <gems/vfs_font.h>
|
||||||
#include <gems/cached_font.h>
|
#include <gems/cached_font.h>
|
||||||
@ -72,6 +73,9 @@ struct Terminal::Main : Character_consumer
|
|||||||
|
|
||||||
Color_palette _color_palette { };
|
Color_palette _color_palette { };
|
||||||
|
|
||||||
|
Constructible<Attached_rom_dataspace> _clipboard_rom { };
|
||||||
|
Constructible<Expanding_reporter> _clipboard_reporter { };
|
||||||
|
|
||||||
void _handle_config();
|
void _handle_config();
|
||||||
|
|
||||||
Signal_handler<Main> _config_handler {
|
Signal_handler<Main> _config_handler {
|
||||||
@ -82,6 +86,14 @@ struct Terminal::Main : Character_consumer
|
|||||||
|
|
||||||
Framebuffer _framebuffer { _env, _config_handler };
|
Framebuffer _framebuffer { _env, _config_handler };
|
||||||
|
|
||||||
|
Point _pointer { }; /* pointer positon in pixels */
|
||||||
|
|
||||||
|
bool _shift_pressed = false;
|
||||||
|
|
||||||
|
bool _selecting = false;
|
||||||
|
|
||||||
|
struct Paste_buffer { char buffer[READ_BUFFER_SIZE]; } _paste_buffer { };
|
||||||
|
|
||||||
typedef Pixel_rgb565 PT;
|
typedef Pixel_rgb565 PT;
|
||||||
|
|
||||||
Constructible<Text_screen_surface<PT>> _text_screen_surface { };
|
Constructible<Text_screen_surface<PT>> _text_screen_surface { };
|
||||||
@ -139,6 +151,9 @@ struct Terminal::Main : Character_consumer
|
|||||||
Signal_handler<Main> _input_handler {
|
Signal_handler<Main> _input_handler {
|
||||||
_env.ep(), *this, &Main::_handle_input };
|
_env.ep(), *this, &Main::_handle_input };
|
||||||
|
|
||||||
|
void _report_clipboard_selection();
|
||||||
|
void _paste_clipboard_content();
|
||||||
|
|
||||||
Main(Env &env) : _env(env)
|
Main(Env &env) : _env(env)
|
||||||
{
|
{
|
||||||
_timer .sigh(_flush_handler);
|
_timer .sigh(_flush_handler);
|
||||||
@ -170,6 +185,12 @@ void Terminal::Main::_handle_config()
|
|||||||
|
|
||||||
_font.construct(_heap, _root_dir, cache_limit);
|
_font.construct(_heap, _root_dir, cache_limit);
|
||||||
|
|
||||||
|
_clipboard_reporter.conditional(config.attribute_value("copy", false),
|
||||||
|
_env, "clipboard", "clipboard");
|
||||||
|
|
||||||
|
_clipboard_rom.conditional(config.attribute_value("paste", false),
|
||||||
|
_env, "clipboard");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Adapt terminal to font or framebuffer mode changes
|
* Adapt terminal to font or framebuffer mode changes
|
||||||
*/
|
*/
|
||||||
@ -252,6 +273,56 @@ void Terminal::Main::_handle_input()
|
|||||||
{
|
{
|
||||||
_input.for_each_event([&] (Input::Event const &event) {
|
_input.for_each_event([&] (Input::Event const &event) {
|
||||||
|
|
||||||
|
event.handle_absolute_motion([&] (int x, int y) {
|
||||||
|
|
||||||
|
_pointer = Point(x, y);
|
||||||
|
|
||||||
|
if (_shift_pressed) {
|
||||||
|
_text_screen_surface->pointer(_pointer);
|
||||||
|
_schedule_flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_selecting) {
|
||||||
|
_text_screen_surface->define_selection(_pointer);
|
||||||
|
_schedule_flush();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (event.key_press(Input::KEY_LEFTSHIFT)) {
|
||||||
|
if (_clipboard_reporter.constructed()) {
|
||||||
|
_shift_pressed = true;
|
||||||
|
_text_screen_surface->clear_selection();
|
||||||
|
_text_screen_surface->pointer(_pointer);
|
||||||
|
_schedule_flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key_release(Input::KEY_LEFTSHIFT)) {
|
||||||
|
_shift_pressed = false;
|
||||||
|
_text_screen_surface->pointer(Point(-1, -1));
|
||||||
|
_schedule_flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key_press(Input::BTN_LEFT)) {
|
||||||
|
if (_shift_pressed) {
|
||||||
|
_selecting = true;
|
||||||
|
_text_screen_surface->start_selection(_pointer);
|
||||||
|
} else {
|
||||||
|
_text_screen_surface->clear_selection();
|
||||||
|
}
|
||||||
|
_schedule_flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key_release(Input::BTN_LEFT)) {
|
||||||
|
if (_selecting) {
|
||||||
|
_selecting = false;
|
||||||
|
_report_clipboard_selection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key_press(Input::BTN_MIDDLE))
|
||||||
|
_paste_clipboard_content();
|
||||||
|
|
||||||
event.handle_press([&] (Input::Keycode, Codepoint codepoint) {
|
event.handle_press([&] (Input::Keycode, Codepoint codepoint) {
|
||||||
|
|
||||||
/* function-key unicodes */
|
/* function-key unicodes */
|
||||||
@ -304,4 +375,56 @@ void Terminal::Main::_handle_input()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Terminal::Main::_report_clipboard_selection()
|
||||||
|
{
|
||||||
|
if (!_clipboard_reporter.constructed())
|
||||||
|
return;
|
||||||
|
|
||||||
|
_clipboard_reporter->generate([&] (Xml_generator &xml) {
|
||||||
|
_text_screen_surface->for_each_selected_character([&] (Codepoint c) {
|
||||||
|
String<10> const utf8(c);
|
||||||
|
if (utf8.valid())
|
||||||
|
xml.append_sanitized(utf8.string(), utf8.length() - 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Terminal::Main::_paste_clipboard_content()
|
||||||
|
{
|
||||||
|
if (!_clipboard_rom.constructed())
|
||||||
|
return;
|
||||||
|
|
||||||
|
_clipboard_rom->update();
|
||||||
|
|
||||||
|
_paste_buffer = { };
|
||||||
|
|
||||||
|
/* leave last byte as zero-termination in tact */
|
||||||
|
size_t const max_len = sizeof(_paste_buffer.buffer) - 1;
|
||||||
|
size_t const len =
|
||||||
|
_clipboard_rom->xml().decoded_content(_paste_buffer.buffer, max_len);
|
||||||
|
|
||||||
|
if (len == max_len) {
|
||||||
|
warning("clipboard content exceeds paste buffer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len >= (size_t)_read_buffer.avail_capacity()) {
|
||||||
|
warning("clipboard content exceeds read-buffer capacity");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Utf8_ptr utf8(_paste_buffer.buffer); utf8.complete(); utf8 = utf8.next()) {
|
||||||
|
|
||||||
|
Codepoint const c = utf8.codepoint();
|
||||||
|
|
||||||
|
/* filter out control characters */
|
||||||
|
if (c.value < 32 && c.value != 10)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
_read_buffer.add(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Component::construct(Genode::Env &env) { static Terminal::Main main(env); }
|
void Component::construct(Genode::Env &env) { static Terminal::Main main(env); }
|
||||||
|
@ -91,6 +91,17 @@ class Terminal::Text_screen_surface
|
|||||||
Point start() const { return Point(1, 1); }
|
Point start() const { return Point(1, 1); }
|
||||||
|
|
||||||
bool valid() const { return columns*lines > 0; }
|
bool valid() const { return columns*lines > 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return character position at given pixel coordinates
|
||||||
|
*/
|
||||||
|
Position position(Point p) const
|
||||||
|
{
|
||||||
|
if (char_width.value == 0 || char_height == 0)
|
||||||
|
return Position { };
|
||||||
|
|
||||||
|
return Position((p.x() << 8) / char_width.value, p.y() / char_height);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -133,6 +144,29 @@ class Terminal::Text_screen_surface
|
|||||||
|
|
||||||
Decoder _decoder { _character_screen };
|
Decoder _decoder { _character_screen };
|
||||||
|
|
||||||
|
struct Selection
|
||||||
|
{
|
||||||
|
Position start { };
|
||||||
|
Position end { };
|
||||||
|
|
||||||
|
bool defined = false;
|
||||||
|
|
||||||
|
bool selected(Position pos) const
|
||||||
|
{
|
||||||
|
return defined && pos.in_range(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FN>
|
||||||
|
void for_each_line(FN const &fn) const
|
||||||
|
{
|
||||||
|
for (int i = min(start.y, end.y); i <= max(start.y, end.y); i++)
|
||||||
|
fn(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
} _selection { };
|
||||||
|
|
||||||
|
Position _pointer { -1, -1 };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -199,7 +233,20 @@ class Terminal::Text_screen_surface
|
|||||||
|
|
||||||
Char_cell const cell = _cell_array.get_cell(column, line);
|
Char_cell const cell = _cell_array.get_cell(column, line);
|
||||||
|
|
||||||
_font.apply_glyph(cell.codepoint(), [&] (Glyph_painter::Glyph const &glyph) {
|
Codepoint codepoint = cell.codepoint();
|
||||||
|
|
||||||
|
/* display absent codepoints as whitespace */
|
||||||
|
bool const codepoint_valid = (codepoint.value != 0);
|
||||||
|
|
||||||
|
bool const selected = _selection.selected(Position(column, line))
|
||||||
|
&& codepoint_valid;
|
||||||
|
|
||||||
|
bool const pointer = (_pointer == Position(column, line));
|
||||||
|
|
||||||
|
if (!codepoint_valid)
|
||||||
|
codepoint = Codepoint{' '};
|
||||||
|
|
||||||
|
_font.apply_glyph(codepoint, [&] (Glyph_painter::Glyph const &glyph) {
|
||||||
|
|
||||||
Color_palette::Highlighted const highlighted { cell.highlight() };
|
Color_palette::Highlighted const highlighted { cell.highlight() };
|
||||||
|
|
||||||
@ -216,6 +263,16 @@ class Terminal::Text_screen_surface
|
|||||||
Color fg_color = _palette.foreground(fg_idx, highlighted);
|
Color fg_color = _palette.foreground(fg_idx, highlighted);
|
||||||
Color bg_color = _palette.background(bg_idx, highlighted);
|
Color bg_color = _palette.background(bg_idx, highlighted);
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
bg_color = Color(180, 180, 180);
|
||||||
|
fg_color = Color( 50, 50, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pointer) {
|
||||||
|
bg_color = Color(220, 220, 220);
|
||||||
|
fg_color = Color( 50, 50, 50);
|
||||||
|
}
|
||||||
|
|
||||||
if (cell.has_cursor()) {
|
if (cell.has_cursor()) {
|
||||||
fg_color = Color( 63, 63, 63);
|
fg_color = Color( 63, 63, 63);
|
||||||
bg_color = Color(255, 255, 255);
|
bg_color = Color(255, 255, 255);
|
||||||
@ -271,6 +328,8 @@ class Terminal::Text_screen_surface
|
|||||||
|
|
||||||
void apply_character(Character c)
|
void apply_character(Character c)
|
||||||
{
|
{
|
||||||
|
clear_selection();
|
||||||
|
|
||||||
/* submit character to sequence decoder */
|
/* submit character to sequence decoder */
|
||||||
_decoder.insert(c);
|
_decoder.insert(c);
|
||||||
}
|
}
|
||||||
@ -284,6 +343,110 @@ class Terminal::Text_screen_surface
|
|||||||
* Return size in colums/rows
|
* Return size in colums/rows
|
||||||
*/
|
*/
|
||||||
Area size() const { return _geometry.size(); }
|
Area size() const { return _geometry.size(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set pointer position in pixels (to show the cursor)
|
||||||
|
*/
|
||||||
|
void pointer(Point pointer)
|
||||||
|
{
|
||||||
|
auto position_valid = [&] (Position pos) {
|
||||||
|
return pos.y >= 0 && pos.y < (int)_geometry.lines; };
|
||||||
|
|
||||||
|
/* update old position */
|
||||||
|
if (position_valid(_pointer))
|
||||||
|
_cell_array.mark_line_as_dirty(_pointer.y);
|
||||||
|
|
||||||
|
_pointer = _geometry.position(pointer);
|
||||||
|
|
||||||
|
/* update new position */
|
||||||
|
if (position_valid(_pointer))
|
||||||
|
_cell_array.mark_line_as_dirty(_pointer.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set anchor point of selection
|
||||||
|
*
|
||||||
|
* \param pointer pointer position in pixels
|
||||||
|
*/
|
||||||
|
void start_selection(Point pointer)
|
||||||
|
{
|
||||||
|
if (_selection.defined)
|
||||||
|
clear_selection();
|
||||||
|
|
||||||
|
_selection.start = _geometry.position(pointer);
|
||||||
|
|
||||||
|
define_selection(pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set end position of current selection
|
||||||
|
*
|
||||||
|
* \param pointer pointer position in pixels
|
||||||
|
*/
|
||||||
|
void define_selection(Point pointer)
|
||||||
|
{
|
||||||
|
_selection.for_each_line([&] (int line) {
|
||||||
|
_cell_array.mark_line_as_dirty(line); });
|
||||||
|
|
||||||
|
_selection.end = _geometry.position(pointer);
|
||||||
|
_selection.defined = true;
|
||||||
|
|
||||||
|
_selection.for_each_line([&] (int line) {
|
||||||
|
_cell_array.mark_line_as_dirty(line); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_selection()
|
||||||
|
{
|
||||||
|
if (!_selection.defined)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_selection.for_each_line([&] (int line) {
|
||||||
|
_cell_array.mark_line_as_dirty(line); });
|
||||||
|
|
||||||
|
_selection.defined = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FN>
|
||||||
|
void for_each_selected_character(FN const &fn) const
|
||||||
|
{
|
||||||
|
for (unsigned row = 0; row < _geometry.lines; row++) {
|
||||||
|
bool skip_remaining_chars_on_row = false;
|
||||||
|
|
||||||
|
for (unsigned column = 0; column < _geometry.columns; column++) {
|
||||||
|
|
||||||
|
if (skip_remaining_chars_on_row)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!_selection.selected(Position(column, row)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Codepoint const c { _cell_array.get_cell(column, row).value };
|
||||||
|
|
||||||
|
if (c.value == 0) {
|
||||||
|
|
||||||
|
auto remaining_line_empty = [&] ()
|
||||||
|
{
|
||||||
|
for (unsigned i = column + 1; i < _geometry.columns; i++)
|
||||||
|
if (_cell_array.get_cell(i, row).value)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* generate one line break at the end of a selected line */
|
||||||
|
if (remaining_line_empty()) {
|
||||||
|
fn(Codepoint{'\n'});
|
||||||
|
skip_remaining_chars_on_row = true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
fn(Codepoint{' '});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fn(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _TEXT_SCREEN_SURFACE_H_ */
|
#endif /* _TEXT_SCREEN_SURFACE_H_ */
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
struct Char_cell
|
struct Char_cell
|
||||||
{
|
{
|
||||||
Genode::uint16_t value { ' ' };
|
Genode::uint16_t value { 0 };
|
||||||
|
|
||||||
unsigned char attr;
|
unsigned char attr;
|
||||||
unsigned char color;
|
unsigned char color;
|
||||||
|
@ -76,6 +76,23 @@ struct Terminal::Position
|
|||||||
bool operator != (Position const &pos) const {
|
bool operator != (Position const &pos) const {
|
||||||
return (pos.x != x) || (pos.y != y); }
|
return (pos.x != x) || (pos.y != y); }
|
||||||
|
|
||||||
|
bool operator >= (Position const &other) const
|
||||||
|
{
|
||||||
|
if (y > other.y)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (y == other.y && x >= other.x)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool in_range(Position start, Position end) const
|
||||||
|
{
|
||||||
|
return (end >= start) ? *this >= start && end >= *this
|
||||||
|
: *this >= end && start >= *this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if position lies within the specified boundaries
|
* Return true if position lies within the specified boundaries
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user