terminal: use GUI session directly

Issue #3196
Fixes #3970
This commit is contained in:
Norman Feske
2020-12-22 15:49:28 +01:00
parent 887fcecf63
commit d47f87a768
22 changed files with 132 additions and 278 deletions

View File

@ -1,5 +1,18 @@
This is a graphical terminal implementation. It provides the Terminal
service and uses a nitpicker session for screen representation.
service and uses a GUI session. The font information must be provided
via a VFS configuration like the following.
! <config>
! ...
! <vfs>
! <rom name="VeraMono.ttf"/>
! <dir name="fonts">
! <dir name="monospace">
! <ttf name="regular" path="/VeraMono.ttf" size_px="16"/>
! </dir>
! </dir>
! </vfs>
! </config>
Color configuration
@ -14,7 +27,7 @@ index 0-7 normal color and index 8-15 bright (bold) colors.
! <color index="0" value="#000000"/> <!-- black is real black -->
! <color index="8" value="#101010"/> <!-- bright black stands out a bit -->
! </palette>
! ...
! ...
! </config>
@ -31,3 +44,18 @@ 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.
Initial terminal size
~~~~~~~~~~~~~~~~~~~~~
The terminal adjusts itself to the mode provided by the used GUI service and
responds to mode changes. As a special case, the initial terminal size can be
explicitly configured to accommodate situations where the terminal is hosted
in a window smaller than the screen.
! <config>
! <initial width="800" height="600"/>
! ...
! </config>

View File

@ -1,88 +0,0 @@
/*
* \brief Terminal framebuffer output backend
* \author Norman Feske
* \date 2018-02-06
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _FRAMEBUFFER_H_
#define _FRAMEBUFFER_H_
/* Genode includes */
#include <framebuffer_session/connection.h>
/* local includes */
#include "types.h"
namespace Terminal { class Framebuffer; }
class Terminal::Framebuffer
{
private:
Env &_env;
::Framebuffer::Connection _fb { _env, ::Framebuffer::Mode { } };
Constructible<Attached_dataspace> _ds { };
::Framebuffer::Mode _mode { };
public:
/**
* Constructor
*
* \param mode_sigh signal handler to be triggered on mode changes
*/
Framebuffer(Env &env, Signal_context_capability mode_sigh) : _env(env)
{
switch_to_new_mode();
_fb.mode_sigh(mode_sigh);
}
unsigned w() const { return _mode.area.w(); }
unsigned h() const { return _mode.area.h(); }
template <typename PT>
PT *pixel() { return _ds->local_addr<PT>(); }
void refresh(Rect rect)
{
_fb.refresh(rect.x1(), rect.y1(), rect.w(), rect.h());
}
/**
* Return true if the framebuffer mode differs from the current
* terminal size.
*/
bool mode_changed() const
{
return _fb.mode().area != _mode.area;
}
void switch_to_new_mode()
{
/*
* The mode information must be obtained before updating the
* dataspace to ensure that the mode is consistent with the
* obtained version of the dataspace.
*
* Otherwise - if the server happens to change the mode just after
* the dataspace update - the mode information may correspond to
* the next pending mode at the server while we are operating on
* the old (possibly too small) dataspace.
*/
_mode = _fb.mode();
if (_mode.area.count() > 0)
_ds.construct(_env.rm(), _fb.dataspace());
}
};
#endif /* _FRAMEBUFFER_H_ */

View File

@ -15,8 +15,7 @@
#include <base/component.h>
#include <base/log.h>
#include <base/heap.h>
#include <framebuffer_session/connection.h>
#include <input_session/connection.h>
#include <gui_session/connection.h>
#include <timer_session/connection.h>
#include <base/attached_rom_dataspace.h>
#include <base/attached_ram_dataspace.h>
@ -81,10 +80,23 @@ struct Terminal::Main : Character_consumer
Signal_handler<Main> _config_handler {
_env.ep(), *this, &Main::_handle_config };
Input::Connection _input { _env };
void _handle_mode_change()
{
_fb_mode = _gui.mode();
_handle_config();
}
Signal_handler<Main> _mode_change_handler {
_env.ep(), *this, &Main::_handle_mode_change };
Gui::Connection _gui { _env };
Timer::Connection _timer { _env };
Framebuffer _framebuffer { _env, _config_handler };
Constructible<Attached_dataspace> _fb_ds { };
Framebuffer::Mode _fb_mode { };
Gui::Session::View_handle _view = _gui.create_view();
Point _pointer { }; /* pointer positon in pixels */
@ -111,12 +123,31 @@ struct Terminal::Main : Character_consumer
bool _flush_scheduled = false;
Framebuffer::Mode _flushed_fb_mode { };
void _handle_flush()
{
_flush_scheduled = false;
if (_text_screen_surface.constructed())
_text_screen_surface->redraw();
if (_text_screen_surface.constructed() && _fb_ds.constructed()) {
Surface<PT> surface(_fb_ds->local_addr<PT>(), _fb_mode.area);
Rect const dirty = _text_screen_surface->redraw(surface);
_gui.framebuffer()->refresh(dirty.x1(), dirty.y1(), dirty.w(), dirty.h());
}
/* update view geometry after mode change */
if (_fb_mode.area != _flushed_fb_mode.area) {
typedef Gui::Session::Command Command;
_gui.enqueue<Command::Geometry>(_view, Rect(Point(0, 0), _fb_mode.area));
_gui.enqueue<Command::To_front>(_view, Gui::Session::View_handle());
_gui.execute();
_flushed_fb_mode = _fb_mode;
}
}
Signal_handler<Main> _flush_handler {
@ -160,7 +191,17 @@ struct Terminal::Main : Character_consumer
{
_timer .sigh(_flush_handler);
_config.sigh(_config_handler);
_input .sigh(_input_handler);
_gui.input()->sigh(_input_handler);
_gui.mode_sigh(_mode_change_handler);
_fb_mode = _gui.mode();
/* apply initial size from config, if provided */
_config.xml().with_sub_node("initial", [&] (Xml_node const &initial) {
_fb_mode.area = Area(initial.attribute_value("width", _fb_mode.area.w()),
initial.attribute_value("height", _fb_mode.area.h()));
});
_handle_config();
@ -196,7 +237,10 @@ void Terminal::Main::_handle_config()
/*
* Adapt terminal to font or framebuffer mode changes
*/
_framebuffer.switch_to_new_mode();
_gui.buffer(_fb_mode, false);
if (_fb_mode.area.count() > 0)
_fb_ds.construct(_env.rm(), _gui.framebuffer()->dataspace());
/*
* Distinguish the case where the framebuffer change affects the character
@ -210,7 +254,7 @@ void Terminal::Main::_handle_config()
*/
try {
Text_screen_surface<PT>::Geometry const new_geometry(_font->font(), _framebuffer);
Text_screen_surface<PT>::Geometry const new_geometry(_font->font(), _fb_mode.area);
bool const reconstruct = !_text_screen_surface.constructed() ||
_text_screen_surface->size() != new_geometry.size();
@ -241,7 +285,7 @@ void Terminal::Main::_handle_config()
: Position();
_text_screen_surface.construct(_heap, _font->font(),
_color_palette, _framebuffer);
_color_palette, _fb_mode.area);
if (snapshot.constructed())
_text_screen_surface->import(*snapshot);
@ -273,7 +317,7 @@ void Terminal::Main::_handle_config()
void Terminal::Main::_handle_input()
{
_input.for_each_event([&] (Input::Event const &event) {
_gui.input()->for_each_event([&] (Input::Event const &event) {
event.handle_absolute_motion([&] (int x, int y) {

View File

@ -27,7 +27,6 @@
/* local includes */
#include "color_palette.h"
#include "framebuffer.h"
namespace Terminal { template <typename> class Text_screen_surface; }
@ -62,13 +61,13 @@ class Terminal::Text_screen_surface
class Invalid : Genode::Exception { };
Geometry(Font const &font, Framebuffer const &framebuffer)
Geometry(Font const &font, Area initial_fb_size)
:
fb_size(framebuffer.w(), framebuffer.h()),
fb_size(initial_fb_size),
char_width(font.string_width(Utf8_ptr("M"))),
char_height(font.height()),
columns((_visible(framebuffer.w()) << 8)/char_width.value),
lines(_visible(framebuffer.h())/char_height)
columns((_visible(initial_fb_size.w()) << 8)/char_width.value),
lines(_visible(initial_fb_size.h())/char_height)
{
if (columns*lines == 0)
throw Invalid();
@ -146,8 +145,7 @@ class Terminal::Text_screen_surface
Font const &_font;
Color_palette const &_palette;
Framebuffer &_framebuffer;
Geometry _geometry { _font, _framebuffer };
Geometry _geometry;
Cell_array<Char_cell> _cell_array;
Char_cell_array_character_screen _character_screen { _cell_array };
@ -185,11 +183,11 @@ class Terminal::Text_screen_surface
* \throw Geometry::Invalid
*/
Text_screen_surface(Allocator &alloc, Font const &font,
Color_palette &palette, Framebuffer &framebuffer)
Color_palette &palette, Area initial_fb_size)
:
_font(font),
_palette(palette),
_framebuffer(framebuffer),
_geometry(font, initial_fb_size),
_cell_array(_geometry.columns, _geometry.lines, alloc)
{ }
@ -210,12 +208,8 @@ class Terminal::Text_screen_surface
void cursor_pos(Position pos) { _character_screen.cursor_pos(pos); }
void redraw()
Rect redraw(Surface<PT> &surface)
{
PT *fb_base = _framebuffer.pixel<PT>();
Surface<PT> surface(fb_base, _geometry.fb_size);
unsigned const fg_alpha = 255;
/* clear border */
@ -303,7 +297,7 @@ class Terminal::Text_screen_surface
x.value += (_geometry.char_width.value - (int)((glyph.width - 1)<<8)) >> 1;
Glyph_painter::paint(Glyph_painter::Position(x, (int)y),
glyph, fb_base, _geometry.fb_size.w(),
glyph, surface.addr(), _geometry.fb_size.w(),
clip_top, clip_bottom, clip_left, clip_right,
pixel, fg_alpha);
x = next_x;
@ -331,9 +325,11 @@ class Terminal::Text_screen_surface
+ first_dirty_line*_geometry.char_height;
unsigned const h = num_dirty_lines*_geometry.char_height
+ _geometry.unused_pixels().h();
_framebuffer.refresh(Rect(Point(0, y),
Area(_geometry.fb_size.w(), h)));
return Rect(Point(0, y), Area(_geometry.fb_size.w(),h));
}
return Rect();
}
void apply_character(Character c)