2011-12-22 16:19:25 +01:00
|
|
|
/*
|
|
|
|
* \brief Terminal service
|
|
|
|
* \author Norman Feske
|
|
|
|
* \date 2011-08-11
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2019-02-08 01:26:21 +01:00
|
|
|
* Copyright (C) 2011-2019 Genode Labs GmbH
|
2011-12-22 16:19:25 +01:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
2017-02-20 13:23:52 +01:00
|
|
|
* under the terms of the GNU Affero General Public License version 3.
|
2011-12-22 16:19:25 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* Genode includes */
|
2017-01-02 16:39:55 +01:00
|
|
|
#include <base/component.h>
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 19:07:09 +02:00
|
|
|
#include <base/log.h>
|
2011-12-22 16:19:25 +01:00
|
|
|
#include <base/heap.h>
|
|
|
|
#include <framebuffer_session/connection.h>
|
|
|
|
#include <input_session/connection.h>
|
|
|
|
#include <timer_session/connection.h>
|
2017-01-02 16:39:55 +01:00
|
|
|
#include <base/attached_rom_dataspace.h>
|
|
|
|
#include <base/attached_ram_dataspace.h>
|
2011-12-22 16:19:25 +01:00
|
|
|
#include <input/event.h>
|
2018-04-11 14:14:20 +02:00
|
|
|
#include <gems/vfs.h>
|
|
|
|
#include <gems/vfs_font.h>
|
|
|
|
#include <gems/cached_font.h>
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2012-10-09 17:28:44 +02:00
|
|
|
/* terminal includes */
|
2011-12-22 16:19:25 +01:00
|
|
|
#include <terminal/decoder.h>
|
|
|
|
#include <terminal/types.h>
|
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
/* local includes */
|
|
|
|
#include "text_screen_surface.h"
|
|
|
|
#include "session.h"
|
2013-12-28 22:29:30 +01:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
namespace Terminal { struct Main; }
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
struct Terminal::Main : Character_consumer
|
2011-12-22 16:19:25 +01:00
|
|
|
{
|
|
|
|
/*
|
2018-02-06 21:32:02 +01:00
|
|
|
* Noncopyable
|
2011-12-22 16:19:25 +01:00
|
|
|
*/
|
2018-02-06 21:32:02 +01:00
|
|
|
Main(Main const &);
|
|
|
|
Main &operator = (Main const &);
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
Env &_env;
|
2012-10-09 17:28:44 +02:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
Attached_rom_dataspace _config { _env, "config" };
|
2013-02-20 22:51:34 +01:00
|
|
|
|
2018-04-11 14:14:20 +02:00
|
|
|
Heap _heap { _env.ram(), _env.rm() };
|
2013-02-20 22:51:34 +01:00
|
|
|
|
2018-04-11 14:14:20 +02:00
|
|
|
Root_directory _root_dir { _env, _heap, _config.xml().sub_node("vfs") };
|
2018-03-08 18:27:42 +01:00
|
|
|
|
2018-04-11 14:14:20 +02:00
|
|
|
Cached_font::Limit _font_cache_limit { 0 };
|
|
|
|
|
|
|
|
struct Font
|
|
|
|
{
|
|
|
|
Vfs_font _vfs_font;
|
|
|
|
Cached_font _cached_font;
|
2018-03-08 18:27:42 +01:00
|
|
|
|
2018-04-11 14:14:20 +02:00
|
|
|
Font(Allocator &alloc, Directory &root_dir, Cached_font::Limit limit)
|
|
|
|
:
|
|
|
|
_vfs_font(alloc, root_dir, "fonts/monospace/regular"),
|
|
|
|
_cached_font(alloc, _vfs_font, limit)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
Text_painter::Font const &font() const { return _cached_font; }
|
|
|
|
};
|
|
|
|
|
|
|
|
Constructible<Font> _font { };
|
2013-02-20 22:51:34 +01:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
Color_palette _color_palette { };
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
void _handle_config();
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
Signal_handler<Main> _config_handler {
|
|
|
|
_env.ep(), *this, &Main::_handle_config };
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
Input::Connection _input { _env };
|
|
|
|
Timer::Connection _timer { _env };
|
2012-10-09 15:02:20 +02:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
Framebuffer _framebuffer { _env, _config_handler };
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
typedef Pixel_rgb565 PT;
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
Constructible<Text_screen_surface<PT>> _text_screen_surface { };
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-04-11 14:14:20 +02:00
|
|
|
Area _terminal_size { };
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
/*
|
2018-02-06 21:32:02 +01:00
|
|
|
* Time in milliseconds between a change of the terminal content and the
|
|
|
|
* update of the pixels. By delaying the update, multiple intermediate
|
|
|
|
* changes result in only one rendering step.
|
2011-12-22 16:19:25 +01:00
|
|
|
*/
|
2019-04-09 15:46:36 +02:00
|
|
|
Genode::uint64_t const _flush_delay = 5;
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
bool _flush_scheduled = false;
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-04-11 14:14:20 +02:00
|
|
|
void _handle_flush()
|
2018-02-06 21:32:02 +01:00
|
|
|
{
|
|
|
|
_flush_scheduled = false;
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
if (_text_screen_surface.constructed())
|
|
|
|
_text_screen_surface->redraw();
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
2018-04-11 14:14:20 +02:00
|
|
|
Signal_handler<Main> _flush_handler {
|
|
|
|
_env.ep(), *this, &Main::_handle_flush };
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
void _schedule_flush()
|
|
|
|
{
|
|
|
|
if (!_flush_scheduled) {
|
2019-04-09 15:46:36 +02:00
|
|
|
_timer.trigger_once((Genode::uint64_t)1000*_flush_delay);
|
2018-02-06 21:32:02 +01:00
|
|
|
_flush_scheduled = true;
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
/**
|
|
|
|
* Character_consumer interface, called from 'Terminal::Session_component'
|
|
|
|
*/
|
|
|
|
void consume_character(Character c) override
|
2011-12-23 19:50:28 +01:00
|
|
|
{
|
2018-02-06 21:32:02 +01:00
|
|
|
// XXX distinguish between normal and alternative display mode (smcup)
|
|
|
|
if (_text_screen_surface.constructed())
|
|
|
|
_text_screen_surface->apply_character(c);
|
2011-12-23 19:50:28 +01:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
_schedule_flush();
|
|
|
|
}
|
2012-01-28 00:02:01 +01:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
/* input read buffer */
|
|
|
|
Read_buffer _read_buffer { };
|
2012-01-28 00:02:01 +01:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
/* create root interface for service */
|
2018-02-07 17:03:19 +01:00
|
|
|
Root_component _root { _env, _heap, _read_buffer, *this };
|
2012-01-28 00:02:01 +01:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
void _handle_input();
|
2012-01-28 00:02:01 +01:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
Signal_handler<Main> _input_handler {
|
|
|
|
_env.ep(), *this, &Main::_handle_input };
|
2017-01-02 16:39:55 +01:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
Main(Env &env) : _env(env)
|
|
|
|
{
|
2018-04-11 14:14:20 +02:00
|
|
|
_timer .sigh(_flush_handler);
|
2018-02-07 17:03:19 +01:00
|
|
|
_config.sigh(_config_handler);
|
2018-04-11 14:14:20 +02:00
|
|
|
_input .sigh(_input_handler);
|
2017-01-17 15:45:54 +01:00
|
|
|
|
2018-04-11 14:14:20 +02:00
|
|
|
_handle_config();
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
/* announce service at our parent */
|
|
|
|
_env.parent().announce(_env.ep().manage(_root));
|
|
|
|
}
|
|
|
|
};
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
void Terminal::Main::_handle_config()
|
2011-12-22 16:19:25 +01:00
|
|
|
{
|
2018-02-06 21:32:02 +01:00
|
|
|
_config.update();
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-04-11 14:14:20 +02:00
|
|
|
Xml_node const config = _config.xml();
|
|
|
|
|
2018-06-08 16:52:46 +02:00
|
|
|
_color_palette.apply_config(config);
|
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
_font.destruct();
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-04-11 14:14:20 +02:00
|
|
|
_root_dir.apply_config(config.sub_node("vfs"));
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-04-11 14:14:20 +02:00
|
|
|
Cached_font::Limit const cache_limit {
|
|
|
|
config.attribute_value("cache", Number_of_bytes(256*1024)) };
|
|
|
|
|
|
|
|
_font.construct(_heap, _root_dir, cache_limit);
|
2017-01-02 16:39:55 +01:00
|
|
|
|
|
|
|
/*
|
2018-04-11 14:14:20 +02:00
|
|
|
* Adapt terminal to font or framebuffer mode changes
|
2017-01-02 16:39:55 +01:00
|
|
|
*/
|
2018-02-07 17:03:19 +01:00
|
|
|
_framebuffer.switch_to_new_mode();
|
2018-04-11 14:14:20 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Distinguish the case where the framebuffer change affects the character
|
|
|
|
* grid size from the case where merely the pixel position of the character
|
|
|
|
* grid within the framebuffer changed.
|
|
|
|
*
|
|
|
|
* In the former case, the text-screen surface is reallocated and cleared.
|
|
|
|
* Clients (like ncurses) are expected to respond to a terminal-size change
|
|
|
|
* with a redraw. In the latter case, the client would skip the redraw. So
|
|
|
|
* we need to preserve the content and just reposition the character grid.
|
|
|
|
*/
|
|
|
|
|
|
|
|
try {
|
|
|
|
Text_screen_surface<PT>::Geometry const new_geometry(_font->font(), _framebuffer);
|
|
|
|
|
|
|
|
bool const reconstruct = !_text_screen_surface.constructed() ||
|
|
|
|
_text_screen_surface->size() != new_geometry.size();
|
|
|
|
if (reconstruct) {
|
2018-04-22 19:06:37 +02:00
|
|
|
|
|
|
|
typedef Text_screen_surface<PT>::Snapshot Snapshot;
|
|
|
|
Constructible<Snapshot> snapshot { };
|
|
|
|
|
|
|
|
size_t const snapshot_bytes = _text_screen_surface.constructed()
|
|
|
|
? Snapshot::bytes_needed(*_text_screen_surface)
|
|
|
|
: 0,
|
|
|
|
preserved_bytes = 32*1024,
|
|
|
|
needed_bytes = snapshot_bytes + preserved_bytes,
|
|
|
|
avail_bytes = _env.pd().avail_ram().value;
|
|
|
|
|
|
|
|
bool const preserve_content = (needed_bytes < avail_bytes);
|
|
|
|
|
|
|
|
if (!preserve_content)
|
|
|
|
warning("not enough spare RAM to preserve content (",
|
|
|
|
"need ", Genode::Number_of_bytes(needed_bytes), ", "
|
|
|
|
"have ", Genode::Number_of_bytes(avail_bytes), ")");
|
|
|
|
|
|
|
|
if (preserve_content && _text_screen_surface.constructed())
|
|
|
|
snapshot.construct(_heap, *_text_screen_surface);
|
|
|
|
|
|
|
|
Position const orig_cursor_pos = _text_screen_surface.constructed()
|
|
|
|
? _text_screen_surface->cursor_pos()
|
|
|
|
: Position();
|
|
|
|
|
2018-04-11 14:14:20 +02:00
|
|
|
_text_screen_surface.construct(_heap, _font->font(),
|
|
|
|
_color_palette, _framebuffer);
|
2018-04-22 19:06:37 +02:00
|
|
|
|
|
|
|
if (snapshot.constructed())
|
|
|
|
_text_screen_surface->import(*snapshot);
|
|
|
|
|
|
|
|
_text_screen_surface->cursor_pos(orig_cursor_pos);
|
|
|
|
|
2018-04-11 14:14:20 +02:00
|
|
|
_terminal_size = _text_screen_surface->size();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
_text_screen_surface->geometry(new_geometry);
|
|
|
|
}
|
|
|
|
}
|
2018-05-15 12:15:46 +02:00
|
|
|
catch (Text_screen_surface<PT>::Geometry::Invalid) {
|
2018-04-11 14:14:20 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure to never operate on an invalid-sized framebuffer
|
|
|
|
*
|
|
|
|
* If the exception is thrown by the construction of 'new_geometry',
|
|
|
|
* there may still be a stale '_text_screen_surface'.
|
|
|
|
*/
|
|
|
|
_text_screen_surface.destruct();
|
2018-05-15 12:15:46 +02:00
|
|
|
_terminal_size = Area(0, 0);
|
2018-04-11 14:14:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
_root.notify_resized(Session::Size(_terminal_size.w(), _terminal_size.h()));
|
2018-02-07 17:03:19 +01:00
|
|
|
_schedule_flush();
|
2018-02-06 21:32:02 +01:00
|
|
|
}
|
2017-01-02 16:39:55 +01:00
|
|
|
|
|
|
|
|
|
|
|
void Terminal::Main::_handle_input()
|
|
|
|
{
|
|
|
|
_input.for_each_event([&] (Input::Event const &event) {
|
2017-02-13 13:11:12 +01:00
|
|
|
|
2018-04-20 14:27:45 +02:00
|
|
|
event.handle_press([&] (Input::Keycode, Codepoint codepoint) {
|
|
|
|
|
2018-04-11 14:40:21 +02:00
|
|
|
/* function-key unicodes */
|
|
|
|
enum {
|
|
|
|
CODEPOINT_UP = 0xf700, CODEPOINT_DOWN = 0xf701,
|
|
|
|
CODEPOINT_LEFT = 0xf702, CODEPOINT_RIGHT = 0xf703,
|
|
|
|
CODEPOINT_F1 = 0xf704, CODEPOINT_F2 = 0xf705,
|
|
|
|
CODEPOINT_F3 = 0xf706, CODEPOINT_F4 = 0xf707,
|
|
|
|
CODEPOINT_F5 = 0xf708, CODEPOINT_F6 = 0xf709,
|
|
|
|
CODEPOINT_F7 = 0xf70a, CODEPOINT_F8 = 0xf70b,
|
|
|
|
CODEPOINT_F9 = 0xf70c, CODEPOINT_F10 = 0xf70d,
|
|
|
|
CODEPOINT_F11 = 0xf70e, CODEPOINT_F12 = 0xf70f,
|
|
|
|
CODEPOINT_HOME = 0xf729, CODEPOINT_INSERT = 0xf727,
|
|
|
|
CODEPOINT_DELETE = 0xf728, CODEPOINT_END = 0xf72b,
|
|
|
|
CODEPOINT_PAGEUP = 0xf72c, CODEPOINT_PAGEDOWN = 0xf72d,
|
|
|
|
};
|
|
|
|
|
|
|
|
char const *special_sequence = nullptr;
|
|
|
|
switch (codepoint.value) {
|
|
|
|
case CODEPOINT_UP: special_sequence = "\EOA"; break;
|
|
|
|
case CODEPOINT_DOWN: special_sequence = "\EOB"; break;
|
|
|
|
case CODEPOINT_LEFT: special_sequence = "\EOD"; break;
|
|
|
|
case CODEPOINT_RIGHT: special_sequence = "\EOC"; break;
|
|
|
|
case CODEPOINT_F1: special_sequence = "\EOP"; break;
|
|
|
|
case CODEPOINT_F2: special_sequence = "\EOQ"; break;
|
|
|
|
case CODEPOINT_F3: special_sequence = "\EOR"; break;
|
|
|
|
case CODEPOINT_F4: special_sequence = "\EOS"; break;
|
|
|
|
case CODEPOINT_F5: special_sequence = "\E[15~"; break;
|
|
|
|
case CODEPOINT_F6: special_sequence = "\E[17~"; break;
|
|
|
|
case CODEPOINT_F7: special_sequence = "\E[18~"; break;
|
|
|
|
case CODEPOINT_F8: special_sequence = "\E[19~"; break;
|
|
|
|
case CODEPOINT_F9: special_sequence = "\E[20~"; break;
|
|
|
|
case CODEPOINT_F10: special_sequence = "\E[21~"; break;
|
|
|
|
case CODEPOINT_F11: special_sequence = "\E[23~"; break;
|
|
|
|
case CODEPOINT_F12: special_sequence = "\E[24~"; break;
|
|
|
|
case CODEPOINT_HOME: special_sequence = "\E[1~"; break;
|
|
|
|
case CODEPOINT_INSERT: special_sequence = "\E[2~"; break;
|
|
|
|
case CODEPOINT_DELETE: special_sequence = "\E[3~"; break;
|
|
|
|
case CODEPOINT_END: special_sequence = "\E[4~"; break;
|
|
|
|
case CODEPOINT_PAGEUP: special_sequence = "\E[5~"; break;
|
|
|
|
case CODEPOINT_PAGEDOWN: special_sequence = "\E[6~"; break;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (special_sequence)
|
|
|
|
_read_buffer.add(special_sequence);
|
|
|
|
else
|
2019-02-08 01:26:21 +01:00
|
|
|
_read_buffer.add(codepoint);
|
2018-04-20 14:27:45 +02:00
|
|
|
});
|
2017-01-02 16:39:55 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-06 21:32:02 +01:00
|
|
|
void Component::construct(Genode::Env &env) { static Terminal::Main main(env); }
|