mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 13:47:56 +00:00
terminal: improve internal structure
This patch reorganizes the terminal's source code to become easier to extend. It also enables the strict warning level.
This commit is contained in:
parent
12c8e51071
commit
96a068f90a
87
repos/gems/src/server/terminal/color_palette.h
Normal file
87
repos/gems/src/server/terminal/color_palette.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* \brief Terminal color palette
|
||||
* \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 _COLOR_PALETTE_H_
|
||||
#define _COLOR_PALETTE_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/color.h>
|
||||
|
||||
/* local includes */
|
||||
#include "types.h"
|
||||
|
||||
namespace Terminal {
|
||||
class Color_palette;
|
||||
}
|
||||
|
||||
|
||||
class Terminal::Color_palette
|
||||
{
|
||||
public:
|
||||
|
||||
struct Index { unsigned value; };
|
||||
|
||||
struct Highlighted { bool value; };
|
||||
|
||||
struct Inverse { bool value; };
|
||||
|
||||
private:
|
||||
|
||||
enum { NUM_COLORS = 16U };
|
||||
|
||||
Color _colors[NUM_COLORS];
|
||||
|
||||
public:
|
||||
|
||||
Color_palette()
|
||||
{
|
||||
_colors[0] = Color( 0, 0, 0); /* black */
|
||||
_colors[1] = Color(255, 128, 128); /* red */
|
||||
_colors[2] = Color(128, 255, 128); /* green */
|
||||
_colors[3] = Color(255, 255, 0); /* yellow */
|
||||
_colors[4] = Color(128, 128, 255); /* blue */
|
||||
_colors[5] = Color(255, 0, 255); /* magenta */
|
||||
_colors[6] = Color( 0, 255, 255); /* cyan */
|
||||
_colors[7] = Color(255, 255, 255); /* white */
|
||||
|
||||
/* the upper portion of the palette contains highlight colors */
|
||||
for (unsigned i = 0; i < NUM_COLORS/2; i++) {
|
||||
Color const col = _colors[i];
|
||||
_colors[i + NUM_COLORS/2] = Color((col.r*2)/3, (col.g*2)/3, (col.b*2)/3);
|
||||
}
|
||||
}
|
||||
|
||||
Color foreground(Index index, Highlighted highlighted, Inverse inverse) const
|
||||
{
|
||||
if (index.value >= NUM_COLORS/2)
|
||||
return Color(0, 0, 0);
|
||||
|
||||
Color const col =
|
||||
_colors[index.value + (highlighted.value ? NUM_COLORS/2 : 0)];
|
||||
|
||||
return (inverse.value) ? Color(col.r/2, col.g/2, col.b/2) : col;
|
||||
}
|
||||
|
||||
Color background(Index index, Highlighted highlighted, Inverse inverse) const
|
||||
{
|
||||
Color const color =
|
||||
_colors[index.value + (highlighted.value ? NUM_COLORS/2 : 0)];
|
||||
|
||||
return (inverse.value) ? Color((color.r + 255)/2,
|
||||
(color.g + 255)/2,
|
||||
(color.b + 255)/2)
|
||||
: color;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _COLOR_PALETTE_H_ */
|
69
repos/gems/src/server/terminal/draw_glyph.h
Normal file
69
repos/gems/src/server/terminal/draw_glyph.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* \brief Function for drawing the glyphs of terminal characters
|
||||
* \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 _DRAW_GLYPH_H_
|
||||
#define _DRAW_GLYPH_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <os/pixel_rgb565.h>
|
||||
|
||||
template <typename PT>
|
||||
inline void draw_glyph(Genode::Color fg_color,
|
||||
Genode::Color bg_color,
|
||||
const unsigned char *glyph_base,
|
||||
unsigned glyph_width,
|
||||
unsigned glyph_img_width,
|
||||
unsigned glyph_img_height,
|
||||
unsigned cell_width,
|
||||
PT *fb_base,
|
||||
unsigned fb_width)
|
||||
{
|
||||
PT fg_pixel(fg_color.r, fg_color.g, fg_color.b);
|
||||
PT bg_pixel(bg_color.r, bg_color.g, bg_color.b);
|
||||
|
||||
unsigned const horizontal_gap = cell_width
|
||||
- Genode::min(glyph_width, cell_width);
|
||||
|
||||
unsigned const left_gap = horizontal_gap / 2;
|
||||
unsigned const right_gap = horizontal_gap - left_gap;
|
||||
|
||||
/*
|
||||
* Clear gaps to the left and right of the character if the character's
|
||||
* with is smaller than the cell width.
|
||||
*/
|
||||
if (horizontal_gap) {
|
||||
|
||||
PT *line = fb_base;
|
||||
for (unsigned y = 0 ; y < glyph_img_height; y++, line += fb_width) {
|
||||
|
||||
for (unsigned x = 0; x < left_gap; x++)
|
||||
line[x] = bg_pixel;
|
||||
|
||||
for (unsigned x = cell_width - right_gap; x < cell_width; x++)
|
||||
line[x] = bg_pixel;
|
||||
}
|
||||
}
|
||||
|
||||
/* center glyph horizontally within its cell */
|
||||
fb_base += left_gap;
|
||||
|
||||
for (unsigned y = 0 ; y < glyph_img_height; y++) {
|
||||
for (unsigned x = 0; x < glyph_width; x++)
|
||||
fb_base[x] = PT::mix(bg_pixel, fg_pixel, glyph_base[x]);
|
||||
|
||||
fb_base += fb_width;
|
||||
glyph_base += glyph_img_width;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* _DRAW_GLYPH_H_ */
|
47
repos/gems/src/server/terminal/font_family.h
Normal file
47
repos/gems/src/server/terminal/font_family.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* \brief Terminal font handling
|
||||
* \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 _FONT_FAMILY_H_
|
||||
#define _FONT_FAMILY_H_
|
||||
|
||||
/* local includes */
|
||||
#include "types.h"
|
||||
|
||||
namespace Terminal {
|
||||
typedef Text_painter::Font Font;
|
||||
class Font_family;
|
||||
}
|
||||
|
||||
|
||||
class Terminal::Font_family
|
||||
{
|
||||
private:
|
||||
|
||||
Font const &_regular;
|
||||
|
||||
public:
|
||||
|
||||
Font_family(Font const ®ular) : _regular(regular) { }
|
||||
|
||||
/**
|
||||
* Return font for specified face
|
||||
*
|
||||
* For now, we do not support font faces other than regular.
|
||||
*/
|
||||
Font const &font(Font_face) const { return _regular; }
|
||||
|
||||
unsigned cell_width() const { return _regular.str_w("m"); }
|
||||
unsigned cell_height() const { return _regular.str_h("m"); }
|
||||
};
|
||||
|
||||
#endif /* _FONT_FAMILY_H_ */
|
80
repos/gems/src/server/terminal/framebuffer.h
Normal file
80
repos/gems/src/server/terminal/framebuffer.h
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* \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.width(); }
|
||||
unsigned h() const { return _mode.height(); }
|
||||
|
||||
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
|
||||
{
|
||||
::Framebuffer::Mode _new_mode = _fb.mode();
|
||||
|
||||
return _new_mode.width() != _mode.width()
|
||||
|| _new_mode.height() != _mode.height();
|
||||
}
|
||||
|
||||
void switch_to_new_mode()
|
||||
{
|
||||
_ds.construct(_env.rm(), _fb.dataspace());
|
||||
_mode = _fb.mode();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _FRAMEBUFFER_H_ */
|
@ -18,528 +18,121 @@
|
||||
#include <framebuffer_session/connection.h>
|
||||
#include <input_session/connection.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <root/component.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <base/attached_ram_dataspace.h>
|
||||
#include <input/event.h>
|
||||
#include <util/color.h>
|
||||
#include <os/pixel_rgb565.h>
|
||||
|
||||
/* terminal includes */
|
||||
#include <terminal/decoder.h>
|
||||
#include <terminal/types.h>
|
||||
#include <terminal/scancode_tracker.h>
|
||||
#include <terminal/keymaps.h>
|
||||
#include <terminal/char_cell_array_character_screen.h>
|
||||
#include <terminal_session/terminal_session.h>
|
||||
|
||||
/* nitpicker graphic back end */
|
||||
#include <nitpicker_gfx/text_painter.h>
|
||||
/* local includes */
|
||||
#include "text_screen_surface.h"
|
||||
#include "session.h"
|
||||
|
||||
namespace Terminal {
|
||||
using namespace Genode;
|
||||
struct Main;
|
||||
}
|
||||
namespace Terminal { struct Main; }
|
||||
|
||||
|
||||
using Genode::Pixel_rgb565;
|
||||
typedef Text_painter::Font Font;
|
||||
|
||||
|
||||
static bool const verbose = false;
|
||||
|
||||
|
||||
inline Pixel_rgb565 blend(Pixel_rgb565 src, int alpha)
|
||||
struct Terminal::Main : Character_consumer
|
||||
{
|
||||
Pixel_rgb565 res;
|
||||
res.pixel = ((((alpha >> 3) * (src.pixel & 0xf81f)) >> 5) & 0xf81f)
|
||||
| ((( alpha * (src.pixel & 0x07c0)) >> 8) & 0x07c0);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
inline Pixel_rgb565 mix(Pixel_rgb565 p1, Pixel_rgb565 p2, int alpha)
|
||||
{
|
||||
Pixel_rgb565 res;
|
||||
|
||||
/*
|
||||
* We substract the alpha from 264 instead of 255 to
|
||||
* compensate the brightness loss caused by the rounding
|
||||
* error of the blend function when having only 5 bits
|
||||
* per channel.
|
||||
* Noncopyable
|
||||
*/
|
||||
res.pixel = blend(p1, 264 - alpha).pixel + blend(p2, alpha).pixel;
|
||||
return res;
|
||||
}
|
||||
Main(Main const &);
|
||||
Main &operator = (Main const &);
|
||||
|
||||
Env &_env;
|
||||
|
||||
using Genode::Color;
|
||||
Attached_rom_dataspace _config { _env, "config" };
|
||||
|
||||
|
||||
static Color color_palette[2*8];
|
||||
|
||||
|
||||
static Color foreground_color(Char_cell const &cell)
|
||||
{
|
||||
Color col = color_palette[cell.colidx_fg() + (cell.highlight() ? 8 : 0)];
|
||||
|
||||
if (cell.inverse())
|
||||
col = Color(col.r/2, col.g/2, col.b/2);
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
|
||||
static Color background_color(Char_cell const &cell)
|
||||
{
|
||||
Color col = color_palette[cell.colidx_bg() + (cell.highlight() ? 8 : 0)];
|
||||
|
||||
if (cell.inverse())
|
||||
return Color((col.r + 255)/2, (col.g + 255)/2, (col.b + 255)/2);
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
|
||||
class Font_family
|
||||
{
|
||||
private:
|
||||
|
||||
Font const &_regular;
|
||||
|
||||
public:
|
||||
|
||||
Font_family(Font const ®ular /* ...to be extended */ )
|
||||
: _regular(regular) { }
|
||||
|
||||
/**
|
||||
* Return font for specified face
|
||||
*
|
||||
* For now, we do not support font faces other than regular.
|
||||
*/
|
||||
Font const *font(Font_face) const { return &_regular; }
|
||||
|
||||
unsigned cell_width() const { return _regular.str_w("m"); }
|
||||
unsigned cell_height() const { return _regular.str_h("m"); }
|
||||
};
|
||||
|
||||
|
||||
template <typename PT>
|
||||
inline void draw_glyph(Color fg_color,
|
||||
Color bg_color,
|
||||
const unsigned char *glyph_base,
|
||||
unsigned glyph_width,
|
||||
unsigned glyph_img_width,
|
||||
unsigned glyph_img_height,
|
||||
unsigned cell_width,
|
||||
PT *fb_base,
|
||||
unsigned fb_width)
|
||||
{
|
||||
PT fg_pixel(fg_color.r, fg_color.g, fg_color.b);
|
||||
PT bg_pixel(bg_color.r, bg_color.g, bg_color.b);
|
||||
|
||||
unsigned const horizontal_gap = cell_width
|
||||
- Genode::min(glyph_width, cell_width);
|
||||
|
||||
unsigned const left_gap = horizontal_gap / 2;
|
||||
unsigned const right_gap = horizontal_gap - left_gap;
|
||||
|
||||
/*
|
||||
* Clear gaps to the left and right of the character if the character's
|
||||
* with is smaller than the cell width.
|
||||
/**
|
||||
* Return font data according to config
|
||||
*/
|
||||
if (horizontal_gap) {
|
||||
static char const *_font_data(Xml_node config);
|
||||
|
||||
PT *line = fb_base;
|
||||
for (unsigned y = 0 ; y < glyph_img_height; y++, line += fb_width) {
|
||||
Reconstructible<Font> _font { _font_data(_config.xml()) };
|
||||
Reconstructible<Font_family> _font_family { *_font };
|
||||
|
||||
for (unsigned x = 0; x < left_gap; x++)
|
||||
line[x] = bg_pixel;
|
||||
unsigned char *_keymap = Terminal::usenglish_keymap;
|
||||
unsigned char *_shift = Terminal::usenglish_shift;
|
||||
unsigned char *_altgr = nullptr;
|
||||
|
||||
for (unsigned x = cell_width - right_gap; x < cell_width; x++)
|
||||
line[x] = bg_pixel;
|
||||
}
|
||||
}
|
||||
Color_palette _color_palette { };
|
||||
|
||||
/* center glyph horizontally within its cell */
|
||||
fb_base += left_gap;
|
||||
void _handle_config();
|
||||
|
||||
for (unsigned y = 0 ; y < glyph_img_height; y++) {
|
||||
for (unsigned x = 0; x < glyph_width; x++)
|
||||
fb_base[x] = mix(bg_pixel, fg_pixel, glyph_base[x]);
|
||||
Signal_handler<Main> _config_handler {
|
||||
_env.ep(), *this, &Main::_handle_config };
|
||||
|
||||
fb_base += fb_width;
|
||||
glyph_base += glyph_img_width;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename PT>
|
||||
static void convert_char_array_to_pixels(Cell_array<Char_cell> *cell_array,
|
||||
PT *fb_base,
|
||||
unsigned fb_width,
|
||||
unsigned fb_height,
|
||||
Font_family const &font_family)
|
||||
{
|
||||
Font const ®ular_font = *font_family.font(Font_face::REGULAR);
|
||||
unsigned glyph_height = regular_font.img_h,
|
||||
glyph_step_x = regular_font.wtab['m'];
|
||||
|
||||
unsigned y = 0;
|
||||
for (unsigned line = 0; line < cell_array->num_lines(); line++) {
|
||||
|
||||
if (cell_array->line_dirty(line)) {
|
||||
|
||||
if (verbose)
|
||||
Genode::log("convert line ", line);
|
||||
|
||||
unsigned x = 0;
|
||||
for (unsigned column = 0; column < cell_array->num_cols(); column++) {
|
||||
|
||||
Char_cell cell = cell_array->get_cell(column, line);
|
||||
Font const *font = font_family.font(cell.font_face());
|
||||
unsigned char ascii = cell.ascii;
|
||||
|
||||
if (ascii == 0)
|
||||
ascii = ' ';
|
||||
|
||||
unsigned char const *glyph_base = font->img + font->otab[ascii];
|
||||
|
||||
unsigned glyph_width = regular_font.wtab[ascii];
|
||||
|
||||
if (x + glyph_width > fb_width) break;
|
||||
|
||||
Color fg_color = foreground_color(cell);
|
||||
Color bg_color = background_color(cell);
|
||||
|
||||
if (cell.has_cursor()) {
|
||||
fg_color = Color( 63, 63, 63);
|
||||
bg_color = Color(255, 255, 255);
|
||||
}
|
||||
|
||||
draw_glyph<PT>(fg_color, bg_color,
|
||||
glyph_base, glyph_width,
|
||||
(unsigned)font->img_w, (unsigned)font->img_h,
|
||||
glyph_step_x, fb_base + x, fb_width);
|
||||
|
||||
x += glyph_step_x;
|
||||
}
|
||||
}
|
||||
y += glyph_height;
|
||||
fb_base += fb_width*glyph_height;
|
||||
|
||||
if (y + glyph_height > fb_height) break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace Terminal {
|
||||
|
||||
struct Flush_callback : Genode::List<Flush_callback>::Element
|
||||
{
|
||||
virtual void flush() = 0;
|
||||
};
|
||||
|
||||
|
||||
struct Flush_callback_registry
|
||||
{
|
||||
Genode::List<Flush_callback> _list;
|
||||
Genode::Lock _lock;
|
||||
|
||||
void add(Flush_callback *flush_callback)
|
||||
{
|
||||
Genode::Lock::Guard guard(_lock);
|
||||
_list.insert(flush_callback);
|
||||
}
|
||||
|
||||
void remove(Flush_callback *flush_callback)
|
||||
{
|
||||
Genode::Lock::Guard guard(_lock);
|
||||
_list.remove(flush_callback);
|
||||
}
|
||||
|
||||
void flush()
|
||||
{
|
||||
Genode::Lock::Guard guard(_lock);
|
||||
Flush_callback *curr = _list.first();
|
||||
for (; curr; curr = curr->next())
|
||||
curr->flush();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Trigger_flush_callback
|
||||
{
|
||||
virtual void trigger_flush() = 0;
|
||||
};
|
||||
|
||||
|
||||
class Session_component : public Genode::Rpc_object<Session, Session_component>,
|
||||
public Flush_callback
|
||||
{
|
||||
private:
|
||||
|
||||
Read_buffer &_read_buffer;
|
||||
Framebuffer::Session &_framebuffer;
|
||||
|
||||
Flush_callback_registry &_flush_callback_registry;
|
||||
Trigger_flush_callback &_trigger_flush_callback;
|
||||
Genode::Attached_ram_dataspace _io_buffer;
|
||||
Framebuffer::Mode _fb_mode;
|
||||
Genode::Dataspace_capability _fb_ds_cap;
|
||||
unsigned _char_width;
|
||||
unsigned _char_height;
|
||||
unsigned _columns;
|
||||
unsigned _lines;
|
||||
void *_fb_addr;
|
||||
|
||||
/**
|
||||
* Protect '_char_cell_array'
|
||||
*/
|
||||
Genode::Lock _lock;
|
||||
|
||||
Cell_array<Char_cell> _char_cell_array;
|
||||
Char_cell_array_character_screen _char_cell_array_character_screen;
|
||||
Terminal::Decoder _decoder;
|
||||
|
||||
Terminal::Position _last_cursor_pos;
|
||||
|
||||
Font_family const &_font_family;
|
||||
|
||||
/**
|
||||
* Initialize framebuffer-related attributes
|
||||
*/
|
||||
Genode::Dataspace_capability _init_fb()
|
||||
{
|
||||
if (_fb_mode.format() != Framebuffer::Mode::RGB565) {
|
||||
Genode::error("color mode ", _fb_mode, " not supported");
|
||||
return Genode::Dataspace_capability();
|
||||
}
|
||||
|
||||
return _framebuffer.dataspace();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Session_component(Genode::Env &env,
|
||||
Genode::Allocator &alloc,
|
||||
Read_buffer &read_buffer,
|
||||
Framebuffer::Session &framebuffer,
|
||||
Genode::size_t io_buffer_size,
|
||||
Flush_callback_registry &flush_callback_registry,
|
||||
Trigger_flush_callback &trigger_flush_callback,
|
||||
Font_family const &font_family)
|
||||
:
|
||||
_read_buffer(read_buffer), _framebuffer(framebuffer),
|
||||
_flush_callback_registry(flush_callback_registry),
|
||||
_trigger_flush_callback(trigger_flush_callback),
|
||||
_io_buffer(env.ram(), env.rm(), io_buffer_size),
|
||||
_fb_mode(_framebuffer.mode()),
|
||||
_fb_ds_cap(_init_fb()),
|
||||
|
||||
/* take size of space character as character cell size */
|
||||
_char_width(font_family.cell_width()),
|
||||
_char_height(font_family.cell_height()),
|
||||
|
||||
/* compute number of characters fitting on the framebuffer */
|
||||
_columns(_fb_mode.width()/_char_width),
|
||||
_lines(_fb_mode.height()/_char_height),
|
||||
|
||||
_fb_addr(env.rm().attach(_fb_ds_cap)),
|
||||
_char_cell_array(_columns, _lines, &alloc),
|
||||
_char_cell_array_character_screen(_char_cell_array),
|
||||
_decoder(_char_cell_array_character_screen),
|
||||
|
||||
_font_family(font_family)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
log("new terminal session:");
|
||||
log(" framebuffer has mode ", _fb_mode);
|
||||
log(" character size is ", _char_width, "x", _char_height, " pixels");
|
||||
log(" terminal size is ", _columns, "x", _lines, " characters");
|
||||
|
||||
framebuffer.refresh(0, 0, _fb_mode.width(), _fb_mode.height());
|
||||
|
||||
_flush_callback_registry.add(this);
|
||||
}
|
||||
|
||||
~Session_component()
|
||||
{
|
||||
_flush_callback_registry.remove(this);
|
||||
}
|
||||
|
||||
void flush()
|
||||
{
|
||||
Genode::Lock::Guard guard(_lock);
|
||||
|
||||
convert_char_array_to_pixels<Pixel_rgb565>(&_char_cell_array,
|
||||
(Pixel_rgb565 *)_fb_addr,
|
||||
_fb_mode.width(),
|
||||
_fb_mode.height(),
|
||||
_font_family);
|
||||
|
||||
|
||||
int first_dirty_line = 10000,
|
||||
last_dirty_line = -10000;
|
||||
|
||||
for (int line = 0; line < (int)_char_cell_array.num_lines(); line++) {
|
||||
if (!_char_cell_array.line_dirty(line)) continue;
|
||||
|
||||
first_dirty_line = Genode::min(line, first_dirty_line);
|
||||
last_dirty_line = Genode::max(line, last_dirty_line);
|
||||
|
||||
_char_cell_array.mark_line_as_clean(line);
|
||||
}
|
||||
|
||||
int num_dirty_lines = last_dirty_line - first_dirty_line + 1;
|
||||
if (num_dirty_lines > 0)
|
||||
_framebuffer.refresh(0, first_dirty_line*_char_height,
|
||||
_fb_mode.width(),
|
||||
num_dirty_lines*_char_height);
|
||||
}
|
||||
|
||||
|
||||
/********************************
|
||||
** Terminal session interface **
|
||||
********************************/
|
||||
|
||||
Size size() { return Size(_columns, _lines); }
|
||||
|
||||
bool avail() { return !_read_buffer.empty(); }
|
||||
|
||||
Genode::size_t _read(Genode::size_t dst_len)
|
||||
{
|
||||
/* read data, block on first byte if needed */
|
||||
unsigned num_bytes = 0;
|
||||
unsigned char *dst = _io_buffer.local_addr<unsigned char>();
|
||||
Genode::size_t dst_size = Genode::min(_io_buffer.size(), dst_len);
|
||||
|
||||
while (!_read_buffer.empty() && num_bytes < dst_size)
|
||||
dst[num_bytes++] = _read_buffer.get();
|
||||
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
Genode::size_t _write(Genode::size_t num_bytes)
|
||||
{
|
||||
Genode::Lock::Guard guard(_lock);
|
||||
|
||||
unsigned char *src = _io_buffer.local_addr<unsigned char>();
|
||||
|
||||
for (unsigned i = 0; i < num_bytes; i++) {
|
||||
|
||||
/* submit character to sequence decoder */
|
||||
_decoder.insert(src[i]);
|
||||
}
|
||||
_trigger_flush_callback.trigger_flush();
|
||||
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
Genode::Dataspace_capability _dataspace()
|
||||
{
|
||||
return _io_buffer.cap();
|
||||
}
|
||||
|
||||
void connected_sigh(Genode::Signal_context_capability sigh)
|
||||
{
|
||||
/*
|
||||
* Immediately reflect connection-established signal to the
|
||||
* client because the session is ready to use immediately after
|
||||
* creation.
|
||||
*/
|
||||
Genode::Signal_transmitter(sigh).submit();
|
||||
}
|
||||
|
||||
void read_avail_sigh(Genode::Signal_context_capability cap)
|
||||
{
|
||||
_read_buffer.sigh(cap);
|
||||
}
|
||||
|
||||
Genode::size_t read(void *buf, Genode::size_t) { return 0; }
|
||||
Genode::size_t write(void const *buf, Genode::size_t) { return 0; }
|
||||
};
|
||||
|
||||
|
||||
class Root_component : public Genode::Root_component<Session_component>
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Env &_env;
|
||||
Read_buffer &_read_buffer;
|
||||
Framebuffer::Session &_framebuffer;
|
||||
Flush_callback_registry &_flush_callback_registry;
|
||||
Trigger_flush_callback &_trigger_flush_callback;
|
||||
Font_family const &_font_family;
|
||||
|
||||
protected:
|
||||
|
||||
Session_component *_create_session(const char *args)
|
||||
{
|
||||
Genode::log("create terminal session");
|
||||
|
||||
/*
|
||||
* XXX read I/O buffer size from args
|
||||
*/
|
||||
Genode::size_t io_buffer_size = 4096;
|
||||
|
||||
return new (md_alloc())
|
||||
Session_component(_env, *md_alloc(),
|
||||
_read_buffer,
|
||||
_framebuffer,
|
||||
io_buffer_size,
|
||||
_flush_callback_registry,
|
||||
_trigger_flush_callback,
|
||||
_font_family);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Root_component(Genode::Env &env,
|
||||
Genode::Allocator &md_alloc,
|
||||
Read_buffer &read_buffer,
|
||||
Framebuffer::Session &framebuffer,
|
||||
Flush_callback_registry &flush_callback_registry,
|
||||
Trigger_flush_callback &trigger_flush_callback,
|
||||
Font_family const &font_family)
|
||||
:
|
||||
Genode::Root_component<Session_component>(env.ep(), md_alloc),
|
||||
_env(env),
|
||||
_read_buffer(read_buffer), _framebuffer(framebuffer),
|
||||
_flush_callback_registry(flush_callback_registry),
|
||||
_trigger_flush_callback(trigger_flush_callback),
|
||||
_font_family(font_family)
|
||||
{ }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
struct Terminal::Main
|
||||
{
|
||||
Genode::Env &_env;
|
||||
|
||||
Framebuffer::Connection _framebuffer { _env, Framebuffer::Mode() };
|
||||
Input::Connection _input { _env };
|
||||
Timer::Connection _timer { _env };
|
||||
Input::Connection _input { _env };
|
||||
Timer::Connection _timer { _env };
|
||||
|
||||
Heap _heap { _env.ram(), _env.rm() };
|
||||
|
||||
/* input read buffer */
|
||||
Read_buffer _read_buffer;
|
||||
Framebuffer _framebuffer { _env, _config_handler };
|
||||
|
||||
Terminal::Flush_callback_registry _flush_callback_registry;
|
||||
typedef Pixel_rgb565 PT;
|
||||
|
||||
Constructible<Text_screen_surface<PT>> _text_screen_surface { };
|
||||
|
||||
Session::Size _terminal_size { };
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
unsigned const _flush_delay = 5;
|
||||
|
||||
bool _flush_scheduled = false;
|
||||
|
||||
void _handle_flush(Duration)
|
||||
{
|
||||
_flush_scheduled = false;
|
||||
|
||||
// XXX distinguish between normal and alternate display
|
||||
if (_text_screen_surface.constructed())
|
||||
_text_screen_surface->redraw();
|
||||
}
|
||||
|
||||
Timer::One_shot_timeout<Main> _flush_timeout {
|
||||
_timer, *this, &Main::_handle_flush };
|
||||
|
||||
void _schedule_flush()
|
||||
{
|
||||
if (!_flush_scheduled) {
|
||||
_flush_timeout.schedule(Microseconds{1000*_flush_delay});
|
||||
_flush_scheduled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Character_consumer interface, called from 'Terminal::Session_component'
|
||||
*/
|
||||
void consume_character(Character c) override
|
||||
{
|
||||
// XXX distinguish between normal and alternative display mode (smcup)
|
||||
if (_text_screen_surface.constructed())
|
||||
_text_screen_surface->apply_character(c);
|
||||
|
||||
_schedule_flush();
|
||||
}
|
||||
|
||||
/* input read buffer */
|
||||
Read_buffer _read_buffer { };
|
||||
|
||||
/* create root interface for service */
|
||||
Terminal::Root_component _root;
|
||||
Root_component _root { _env, _heap, _read_buffer, *this, _terminal_size };
|
||||
|
||||
Terminal::Scancode_tracker _scancode_tracker;
|
||||
/*
|
||||
* builtin keyboard-layout handling
|
||||
*
|
||||
* \deprecated The keyboard layout should be handled by the input-filter
|
||||
* component.
|
||||
*/
|
||||
Constructible<Scancode_tracker> _scancode_tracker { };
|
||||
|
||||
/* state needed for key-repeat handling */
|
||||
unsigned const _repeat_delay = 250;
|
||||
@ -556,53 +149,10 @@ struct Terminal::Main
|
||||
Timer::One_shot_timeout<Main> _key_repeat_timeout {
|
||||
_timer, *this, &Main::_handle_key_repeat };
|
||||
|
||||
void _handle_flush(Duration);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
unsigned const _flush_delay = 5;
|
||||
|
||||
bool _flush_scheduled = false;
|
||||
|
||||
void _trigger_flush()
|
||||
Main(Env &env) : _env(env)
|
||||
{
|
||||
if (!_flush_scheduled) {
|
||||
_flush_timeout.schedule(Microseconds{1000*_flush_delay});
|
||||
_flush_scheduled = true;
|
||||
}
|
||||
}
|
||||
_handle_config();
|
||||
|
||||
/*
|
||||
* Callback invoked if new terminal content appears
|
||||
*/
|
||||
struct Trigger_flush : Trigger_flush_callback
|
||||
{
|
||||
Main &_main;
|
||||
void trigger_flush() override { _main._trigger_flush(); }
|
||||
Trigger_flush(Main &main) : _main(main) { }
|
||||
} _trigger_flush_callback { *this };
|
||||
|
||||
Timer::One_shot_timeout<Main> _flush_timeout {
|
||||
_timer, *this, &Main::_handle_flush };
|
||||
|
||||
Main(Genode::Env &env,
|
||||
Font_family &font_family,
|
||||
unsigned char const *keymap,
|
||||
unsigned char const *shift,
|
||||
unsigned char const *altgr,
|
||||
unsigned char const *control)
|
||||
:
|
||||
_env(env),
|
||||
_root(_env, _heap,
|
||||
_read_buffer, _framebuffer,
|
||||
_flush_callback_registry,
|
||||
_trigger_flush_callback,
|
||||
font_family),
|
||||
_scancode_tracker(keymap, shift, altgr, Terminal::control)
|
||||
{
|
||||
_input.sigh(_input_handler);
|
||||
|
||||
/* announce service at our parent */
|
||||
@ -611,6 +161,79 @@ struct Terminal::Main
|
||||
};
|
||||
|
||||
|
||||
/* built-in fonts */
|
||||
extern char const _binary_notix_8_tff_start;
|
||||
extern char const _binary_terminus_12_tff_start;
|
||||
extern char const _binary_terminus_16_tff_start;
|
||||
|
||||
|
||||
char const *Terminal::Main::_font_data(Xml_node config)
|
||||
{
|
||||
if (config.has_sub_node("font")) {
|
||||
size_t const size = config.sub_node("font").attribute_value("size", 16U);
|
||||
|
||||
switch (size) {
|
||||
case 8: return &_binary_notix_8_tff_start; break;
|
||||
case 12: return &_binary_terminus_12_tff_start; break;
|
||||
case 16: return &_binary_terminus_16_tff_start; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
return &_binary_terminus_16_tff_start;
|
||||
}
|
||||
|
||||
|
||||
void Terminal::Main::_handle_config()
|
||||
{
|
||||
_config.update();
|
||||
|
||||
_font_family.destruct();
|
||||
_font.destruct();
|
||||
|
||||
Xml_node const config = _config.xml();
|
||||
|
||||
_font.construct(_font_data(config));
|
||||
_font_family.construct(*_font);
|
||||
|
||||
/*
|
||||
* Adapt terminal to framebuffer mode changes
|
||||
*/
|
||||
if (_framebuffer.mode_changed() || !_text_screen_surface.constructed()) {
|
||||
_framebuffer.switch_to_new_mode();
|
||||
_text_screen_surface.construct(_heap, *_font_family,
|
||||
_color_palette, _framebuffer);
|
||||
_terminal_size = _text_screen_surface->size();
|
||||
_schedule_flush();
|
||||
}
|
||||
|
||||
/*
|
||||
* Read keyboard layout from config file
|
||||
*/
|
||||
|
||||
_keymap = Terminal::usenglish_keymap;
|
||||
_shift = Terminal::usenglish_shift;
|
||||
_altgr = nullptr;
|
||||
|
||||
if (config.has_sub_node("keyboard")) {
|
||||
|
||||
if (config.sub_node("keyboard").attribute("layout").has_value("de")) {
|
||||
_keymap = Terminal::german_keymap;
|
||||
_shift = Terminal::german_shift;
|
||||
_altgr = Terminal::german_altgr;
|
||||
}
|
||||
|
||||
if (config.sub_node("keyboard").attribute("layout").has_value("none")) {
|
||||
_keymap = nullptr;
|
||||
_shift = nullptr;
|
||||
_altgr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_scancode_tracker.constructed())
|
||||
_scancode_tracker.construct(_keymap, _shift, _altgr, Terminal::control);
|
||||
}
|
||||
|
||||
|
||||
void Terminal::Main::_handle_input()
|
||||
{
|
||||
_input.for_each_event([&] (Input::Event const &event) {
|
||||
@ -623,15 +246,19 @@ void Terminal::Main::_handle_input()
|
||||
if (utf8.b3) _read_buffer.add(utf8.b3);
|
||||
}
|
||||
|
||||
/* apply the terminal's built-in character map if configured */
|
||||
if (!_scancode_tracker.constructed())
|
||||
return;
|
||||
|
||||
bool press = (event.type() == Input::Event::PRESS ? true : false);
|
||||
bool release = (event.type() == Input::Event::RELEASE ? true : false);
|
||||
int keycode = event.code();
|
||||
|
||||
if (press || release)
|
||||
_scancode_tracker.submit(keycode, press);
|
||||
_scancode_tracker->submit(keycode, press);
|
||||
|
||||
if (press) {
|
||||
_scancode_tracker.emit_current_character(_read_buffer);
|
||||
_scancode_tracker->emit_current_character(_read_buffer);
|
||||
|
||||
/* setup first key repeat */
|
||||
_repeat_next = _repeat_delay;
|
||||
@ -651,7 +278,8 @@ void Terminal::Main::_handle_key_repeat(Duration)
|
||||
if (_repeat_next) {
|
||||
|
||||
/* repeat current character or sequence */
|
||||
_scancode_tracker.emit_current_character(_read_buffer);
|
||||
if (_scancode_tracker.constructed())
|
||||
_scancode_tracker->emit_current_character(_read_buffer);
|
||||
|
||||
_repeat_next = _repeat_rate;
|
||||
}
|
||||
@ -660,89 +288,4 @@ void Terminal::Main::_handle_key_repeat(Duration)
|
||||
}
|
||||
|
||||
|
||||
void Terminal::Main::_handle_flush(Duration)
|
||||
{
|
||||
_flush_scheduled = false;
|
||||
_flush_callback_registry.flush();
|
||||
}
|
||||
|
||||
|
||||
/* built-in fonts */
|
||||
extern char _binary_notix_8_tff_start;
|
||||
extern char _binary_terminus_12_tff_start;
|
||||
extern char _binary_terminus_16_tff_start;
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env)
|
||||
{
|
||||
/* XXX execute constructors of global statics */
|
||||
env.exec_static_constructors();
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
Attached_rom_dataspace config(env, "config");
|
||||
|
||||
/* initialize color palette */
|
||||
color_palette[0] = Color( 0, 0, 0); /* black */
|
||||
color_palette[1] = Color(255, 128, 128); /* red */
|
||||
color_palette[2] = Color(128, 255, 128); /* green */
|
||||
color_palette[3] = Color(255, 255, 0); /* yellow */
|
||||
color_palette[4] = Color(128, 128, 255); /* blue */
|
||||
color_palette[5] = Color(255, 0, 255); /* magenta */
|
||||
color_palette[6] = Color( 0, 255, 255); /* cyan */
|
||||
color_palette[7] = Color(255, 255, 255); /* white */
|
||||
|
||||
/* the upper portion of the palette contains highlight colors */
|
||||
for (int i = 0; i < 8; i++) {
|
||||
Color col = color_palette[i];
|
||||
col = Color((col.r*2)/3, (col.g*2)/3, (col.b*2)/3);
|
||||
color_palette[i + 8] = col;
|
||||
}
|
||||
|
||||
/* pick font according to config file */
|
||||
char const *font_data = &_binary_terminus_16_tff_start;
|
||||
try {
|
||||
size_t font_size = 16;
|
||||
config.xml().sub_node("font").attribute("size").value(&font_size);
|
||||
|
||||
switch (font_size) {
|
||||
case 8: font_data = &_binary_notix_8_tff_start; break;
|
||||
case 12: font_data = &_binary_terminus_12_tff_start; break;
|
||||
case 16: font_data = &_binary_terminus_16_tff_start; break;
|
||||
default: break;
|
||||
}
|
||||
} catch (...) { }
|
||||
|
||||
static Font font(font_data);
|
||||
static Font_family font_family(font);
|
||||
|
||||
log("cell size is ", (int)font_family.cell_width(),
|
||||
"x", (int)font_family.cell_height());
|
||||
|
||||
unsigned char *keymap = Terminal::usenglish_keymap;
|
||||
unsigned char *shift = Terminal::usenglish_shift;
|
||||
unsigned char *altgr = 0;
|
||||
|
||||
/*
|
||||
* Read keyboard layout from config file
|
||||
*/
|
||||
try {
|
||||
if (config.xml().sub_node("keyboard")
|
||||
.attribute("layout").has_value("de")) {
|
||||
keymap = Terminal::german_keymap;
|
||||
shift = Terminal::german_shift;
|
||||
altgr = Terminal::german_altgr;
|
||||
}
|
||||
} catch (...) { }
|
||||
|
||||
try {
|
||||
if (config.xml().sub_node("keyboard")
|
||||
.attribute("layout").has_value("none")) {
|
||||
keymap = nullptr;
|
||||
shift = nullptr;
|
||||
altgr = nullptr;
|
||||
}
|
||||
} catch (...) { }
|
||||
|
||||
static Terminal::Main main(env, font_family, keymap, shift, altgr, Terminal::control);
|
||||
}
|
||||
void Component::construct(Genode::Env &env) { static Terminal::Main main(env); }
|
||||
|
157
repos/gems/src/server/terminal/session.h
Normal file
157
repos/gems/src/server/terminal/session.h
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* \brief Terminal session interface
|
||||
* \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 _SESSION_H_
|
||||
#define _SESSION_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <root/component.h>
|
||||
#include <terminal_session/terminal_session.h>
|
||||
|
||||
/* local includes */
|
||||
#include "types.h"
|
||||
|
||||
namespace Terminal {
|
||||
class Session_component;
|
||||
class Root_component;
|
||||
}
|
||||
|
||||
|
||||
class Terminal::Session_component : public Rpc_object<Session, Session_component>
|
||||
{
|
||||
private:
|
||||
|
||||
Read_buffer &_read_buffer;
|
||||
Character_consumer &_character_consumer;
|
||||
Size const &_terminal_size;
|
||||
Attached_ram_dataspace _io_buffer;
|
||||
|
||||
Terminal::Position _last_cursor_pos { };
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Session_component(Env &env,
|
||||
Read_buffer &read_buffer,
|
||||
Character_consumer &character_consumer,
|
||||
Size const &terminal_size,
|
||||
size_t io_buffer_size)
|
||||
:
|
||||
_read_buffer(read_buffer),
|
||||
_character_consumer(character_consumer),
|
||||
_terminal_size(terminal_size),
|
||||
_io_buffer(env.ram(), env.rm(), io_buffer_size)
|
||||
{ }
|
||||
|
||||
|
||||
/********************************
|
||||
** Terminal session interface **
|
||||
********************************/
|
||||
|
||||
Size size() override { return _terminal_size; }
|
||||
|
||||
bool avail() override { return !_read_buffer.empty(); }
|
||||
|
||||
size_t _read(size_t dst_len)
|
||||
{
|
||||
/* read data, block on first byte if needed */
|
||||
unsigned num_bytes = 0;
|
||||
unsigned char *dst = _io_buffer.local_addr<unsigned char>();
|
||||
size_t dst_size = min(_io_buffer.size(), dst_len);
|
||||
|
||||
while (!_read_buffer.empty() && num_bytes < dst_size)
|
||||
dst[num_bytes++] = _read_buffer.get();
|
||||
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
size_t _write(size_t num_bytes)
|
||||
{
|
||||
unsigned char *src = _io_buffer.local_addr<unsigned char>();
|
||||
|
||||
for (unsigned i = 0; i < num_bytes; i++)
|
||||
_character_consumer.consume_character(src[i]);
|
||||
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
Dataspace_capability _dataspace() { return _io_buffer.cap(); }
|
||||
|
||||
void connected_sigh(Signal_context_capability sigh) override
|
||||
{
|
||||
/*
|
||||
* Immediately reflect connection-established signal to the
|
||||
* client because the session is ready to use immediately after
|
||||
* creation.
|
||||
*/
|
||||
Signal_transmitter(sigh).submit();
|
||||
}
|
||||
|
||||
void read_avail_sigh(Signal_context_capability cap) override
|
||||
{
|
||||
_read_buffer.sigh(cap);
|
||||
}
|
||||
|
||||
size_t read(void *, size_t) override { return 0; }
|
||||
size_t write(void const *, size_t) override { return 0; }
|
||||
};
|
||||
|
||||
|
||||
class Terminal::Root_component : public Genode::Root_component<Session_component>
|
||||
{
|
||||
private:
|
||||
|
||||
Env &_env;
|
||||
Read_buffer &_read_buffer;
|
||||
Character_consumer &_character_consumer;
|
||||
Session::Size &_terminal_size;
|
||||
|
||||
protected:
|
||||
|
||||
Session_component *_create_session(const char *)
|
||||
{
|
||||
/*
|
||||
* XXX read I/O buffer size from args
|
||||
*/
|
||||
size_t io_buffer_size = 4096;
|
||||
|
||||
return new (md_alloc())
|
||||
Session_component(_env,
|
||||
_read_buffer,
|
||||
_character_consumer,
|
||||
_terminal_size,
|
||||
io_buffer_size);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Root_component(Env &env,
|
||||
Allocator &md_alloc,
|
||||
Read_buffer &read_buffer,
|
||||
Character_consumer &character_consumer,
|
||||
Session::Size &terminal_size)
|
||||
:
|
||||
Genode::Root_component<Session_component>(env.ep(), md_alloc),
|
||||
_env(env),
|
||||
_read_buffer(read_buffer),
|
||||
_character_consumer(character_consumer),
|
||||
_terminal_size(terminal_size)
|
||||
{ }
|
||||
};
|
||||
|
||||
#endif /* _SESSION_H_ */
|
@ -2,5 +2,3 @@ TARGET = terminal
|
||||
SRC_CC = main.cc
|
||||
LIBS = base
|
||||
SRC_BIN = $(notdir $(wildcard $(PRG_DIR)/*.tff))
|
||||
|
||||
CC_CXX_WARN_STRICT =
|
||||
|
160
repos/gems/src/server/terminal/text_screen_surface.h
Normal file
160
repos/gems/src/server/terminal/text_screen_surface.h
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* \brief Terminal graphics backend for textual screen
|
||||
* \author Norman Feske
|
||||
* \date 2018-02-06
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-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 _TEXT_SCREEN_SURFACE_H_
|
||||
#define _TEXT_SCREEN_SURFACE_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <os/pixel_rgb565.h>
|
||||
|
||||
/* terminal includes */
|
||||
#include <terminal/char_cell_array_character_screen.h>
|
||||
|
||||
/* nitpicker graphic back end */
|
||||
#include <nitpicker_gfx/text_painter.h>
|
||||
|
||||
/* local includes */
|
||||
#include "font_family.h"
|
||||
#include "draw_glyph.h"
|
||||
#include "color_palette.h"
|
||||
#include "framebuffer.h"
|
||||
|
||||
namespace Terminal { template <typename> class Text_screen_surface; }
|
||||
|
||||
|
||||
template <typename PT>
|
||||
class Terminal::Text_screen_surface
|
||||
{
|
||||
private:
|
||||
|
||||
Font_family const &_font_family;
|
||||
Color_palette const &_palette;
|
||||
Framebuffer &_framebuffer;
|
||||
|
||||
/* take size of space character as character cell size */
|
||||
unsigned const _char_width { _font_family.cell_width() };
|
||||
unsigned const _char_height { _font_family.cell_height() };
|
||||
|
||||
/* number of characters fitting on the framebuffer */
|
||||
unsigned const _columns { _framebuffer.w()/_char_width };
|
||||
unsigned const _lines { _framebuffer.h()/_char_height };
|
||||
|
||||
Cell_array<Char_cell> _cell_array;
|
||||
Char_cell_array_character_screen _character_screen { _cell_array };
|
||||
|
||||
Decoder _decoder { _character_screen };
|
||||
|
||||
public:
|
||||
|
||||
Text_screen_surface(Allocator &alloc, Font_family const &font_family,
|
||||
Color_palette &palette, Framebuffer &framebuffer)
|
||||
:
|
||||
_font_family(font_family),
|
||||
_palette(palette),
|
||||
_framebuffer(framebuffer),
|
||||
_cell_array(_columns, _lines, alloc)
|
||||
{ }
|
||||
|
||||
void redraw()
|
||||
{
|
||||
Font const ®ular_font = _font_family.font(Font_face::REGULAR);
|
||||
|
||||
unsigned const glyph_height = regular_font.img_h,
|
||||
glyph_step_x = regular_font.wtab['m'];
|
||||
|
||||
unsigned const fb_width = _framebuffer.w(),
|
||||
fb_height = _framebuffer.h();
|
||||
|
||||
PT *fb_base = _framebuffer.pixel<PT>();
|
||||
|
||||
unsigned y = 0;
|
||||
for (unsigned line = 0; line < _cell_array.num_lines(); line++) {
|
||||
|
||||
if (_cell_array.line_dirty(line)) {
|
||||
|
||||
unsigned x = 0;
|
||||
for (unsigned column = 0; column < _cell_array.num_cols(); column++) {
|
||||
|
||||
Char_cell cell = _cell_array.get_cell(column, line);
|
||||
Font const &font = _font_family.font(cell.font_face());
|
||||
unsigned char ascii = cell.ascii;
|
||||
|
||||
if (ascii == 0)
|
||||
ascii = ' ';
|
||||
|
||||
unsigned char const *glyph_base = font.img + font.otab[ascii];
|
||||
|
||||
unsigned glyph_width = regular_font.wtab[ascii];
|
||||
|
||||
if (x + glyph_width > fb_width)
|
||||
break;
|
||||
|
||||
Color_palette::Highlighted const highlighted { cell.highlight() };
|
||||
Color_palette::Inverse const inverse { cell.inverse() };
|
||||
|
||||
Color fg_color =
|
||||
_palette.foreground(Color_palette::Index{cell.colidx_fg()},
|
||||
highlighted, inverse);
|
||||
|
||||
Color bg_color =
|
||||
_palette.background(Color_palette::Index{cell.colidx_bg()},
|
||||
highlighted, inverse);
|
||||
|
||||
if (cell.has_cursor()) {
|
||||
fg_color = Color( 63, 63, 63);
|
||||
bg_color = Color(255, 255, 255);
|
||||
}
|
||||
|
||||
draw_glyph<PT>(fg_color, bg_color,
|
||||
glyph_base, glyph_width,
|
||||
(unsigned)font.img_w, (unsigned)font.img_h,
|
||||
glyph_step_x, fb_base + x, fb_width);
|
||||
|
||||
x += glyph_step_x;
|
||||
}
|
||||
}
|
||||
y += glyph_height;
|
||||
fb_base += fb_width*glyph_height;
|
||||
|
||||
if (y + glyph_height > fb_height) break;
|
||||
}
|
||||
|
||||
int first_dirty_line = 10000,
|
||||
last_dirty_line = -10000;
|
||||
|
||||
for (int line = 0; line < (int)_cell_array.num_lines(); line++) {
|
||||
if (!_cell_array.line_dirty(line)) continue;
|
||||
|
||||
first_dirty_line = min(line, first_dirty_line);
|
||||
last_dirty_line = max(line, last_dirty_line);
|
||||
|
||||
_cell_array.mark_line_as_clean(line);
|
||||
}
|
||||
|
||||
int const num_dirty_lines = last_dirty_line - first_dirty_line + 1;
|
||||
if (num_dirty_lines > 0)
|
||||
_framebuffer.refresh(Rect(Point(0, first_dirty_line*_char_height),
|
||||
Area(fb_width,
|
||||
num_dirty_lines*_char_height)));
|
||||
}
|
||||
|
||||
void apply_character(Character c)
|
||||
{
|
||||
/* submit character to sequence decoder */
|
||||
_decoder.insert(c.c);
|
||||
}
|
||||
|
||||
Session::Size size() const { return Session::Size(_columns, _lines); }
|
||||
};
|
||||
|
||||
#endif /* _TEXT_SCREEN_SURFACE_H_ */
|
41
repos/gems/src/server/terminal/types.h
Normal file
41
repos/gems/src/server/terminal/types.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* \brief Common types used by the terminal
|
||||
* \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 _TYPES_H_
|
||||
#define _TYPES_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/interface.h>
|
||||
#include <util/list.h>
|
||||
#include <base/registry.h>
|
||||
#include <os/surface.h>
|
||||
#include <terminal_session/terminal_session.h>
|
||||
|
||||
/* terminal includes */
|
||||
#include <terminal/types.h>
|
||||
|
||||
namespace Terminal {
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
typedef Surface_base::Rect Rect;
|
||||
typedef Surface_base::Area Area;
|
||||
typedef Surface_base::Point Point;
|
||||
|
||||
struct Character_consumer : Interface
|
||||
{
|
||||
virtual void consume_character(Character c) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _TYPES_H_ */
|
@ -220,7 +220,7 @@ class Terminal::Session_component : public Genode::Rpc_object<Session, Session_c
|
||||
_label(label),
|
||||
_session_manager(session_manager),
|
||||
_io_buffer(_env.ram(), _env.rm(), io_buffer_size),
|
||||
_char_cell_array(ncurses.columns(), ncurses.lines() - 1, &heap),
|
||||
_char_cell_array(ncurses.columns(), ncurses.lines() - 1, heap),
|
||||
_char_cell_array_character_screen(_char_cell_array),
|
||||
_decoder(_char_cell_array_character_screen)
|
||||
{
|
||||
|
@ -30,11 +30,17 @@ class Cell_array
|
||||
{
|
||||
private:
|
||||
|
||||
/*
|
||||
* Noncopyable
|
||||
*/
|
||||
Cell_array(Cell_array const &);
|
||||
Cell_array &operator = (Cell_array const &);
|
||||
|
||||
unsigned _num_cols;
|
||||
unsigned _num_lines;
|
||||
Genode::Allocator *_alloc;
|
||||
CELL **_array;
|
||||
bool *_line_dirty;
|
||||
Genode::Allocator &_alloc;
|
||||
CELL **_array = nullptr;
|
||||
bool *_line_dirty = nullptr;
|
||||
|
||||
typedef CELL *Char_cell_line;
|
||||
|
||||
@ -73,7 +79,7 @@ class Cell_array
|
||||
public:
|
||||
|
||||
Cell_array(unsigned num_cols, unsigned num_lines,
|
||||
Genode::Allocator *alloc)
|
||||
Genode::Allocator &alloc)
|
||||
:
|
||||
_num_cols(num_cols),
|
||||
_num_lines(num_lines),
|
||||
|
@ -24,10 +24,10 @@ struct Char_cell
|
||||
unsigned char ascii;
|
||||
unsigned char color;
|
||||
|
||||
enum { ATTR_COLIDX_MASK = 0x07,
|
||||
ATTR_CURSOR = 0x10,
|
||||
ATTR_INVERSE = 0x20,
|
||||
ATTR_HIGHLIGHT = 0x40 };
|
||||
enum { ATTR_COLIDX_MASK = 0x07U,
|
||||
ATTR_CURSOR = 0x10U,
|
||||
ATTR_INVERSE = 0x20U,
|
||||
ATTR_HIGHLIGHT = 0x40U };
|
||||
|
||||
enum { COLOR_MASK = 0x3f }; /* 111111 */
|
||||
|
||||
@ -47,8 +47,8 @@ struct Char_cell
|
||||
return Font_face((Font_face::Type)(attr & Font_face::attr_mask()));
|
||||
}
|
||||
|
||||
int colidx_fg() const { return color & ATTR_COLIDX_MASK; }
|
||||
int colidx_bg() const { return (color >> 3) & ATTR_COLIDX_MASK; }
|
||||
unsigned colidx_fg() const { return color & ATTR_COLIDX_MASK; }
|
||||
unsigned colidx_bg() const { return (color >> 3) & ATTR_COLIDX_MASK; }
|
||||
bool inverse() const { return attr & ATTR_INVERSE; }
|
||||
bool highlight() const { return attr & ATTR_HIGHLIGHT; }
|
||||
|
||||
@ -69,7 +69,8 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
|
||||
|
||||
Cell_array<Char_cell> &_char_cell_array;
|
||||
Terminal::Boundary _boundary;
|
||||
Terminal::Position _cursor_pos;
|
||||
Terminal::Position _cursor_pos { };
|
||||
|
||||
/**
|
||||
* Color index contains the fg color in the first 3 bits
|
||||
* and the bg color in the second 3 bits (0bbbbfff).
|
||||
|
@ -22,7 +22,7 @@ namespace Terminal { struct Character_screen; }
|
||||
/**
|
||||
* Character-screen interface called by input-stream decoder
|
||||
*/
|
||||
struct Terminal::Character_screen
|
||||
struct Terminal::Character_screen : Genode::Interface
|
||||
{
|
||||
virtual void output(Character c) = 0;
|
||||
|
||||
|
@ -140,7 +140,7 @@ class Terminal::Decoder
|
||||
return (index <= _index) ? _entries[index] : Invalid_entry();
|
||||
}
|
||||
|
||||
} _escape_stack;
|
||||
} _escape_stack { };
|
||||
|
||||
enum State {
|
||||
STATE_IDLE,
|
||||
|
@ -30,54 +30,14 @@ class Terminal::Read_buffer : public Genode::Ring_buffer<unsigned char, READ_BUF
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Signal_context_capability _sigh_cap;
|
||||
Genode::Signal_context_capability _sigh_cap { };
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Register signal handler for read-avail signals
|
||||
*/
|
||||
void sigh(Genode::Signal_context_capability cap)
|
||||
{
|
||||
_sigh_cap = cap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add element into read buffer and emit signal
|
||||
*/
|
||||
void add(unsigned char c)
|
||||
{
|
||||
Genode::Ring_buffer<unsigned char, READ_BUFFER_SIZE>::add(c);
|
||||
|
||||
if (_sigh_cap.valid())
|
||||
Genode::Signal_transmitter(_sigh_cap).submit();
|
||||
}
|
||||
|
||||
void add(char const *str)
|
||||
{
|
||||
while (*str)
|
||||
add(*str++);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
enum { READ_BUFFER_SIZE = 4096 };
|
||||
|
||||
class Read_buffer : public Genode::Ring_buffer<unsigned char, READ_BUFFER_SIZE>
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Signal_context_capability _sigh_cap;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Register signal handler for read-avail signals
|
||||
*/
|
||||
void sigh(Genode::Signal_context_capability cap)
|
||||
{
|
||||
_sigh_cap = cap;
|
||||
}
|
||||
void sigh(Genode::Signal_context_capability cap) { _sigh_cap = cap; }
|
||||
|
||||
/**
|
||||
* Add element into read buffer and emit signal
|
||||
|
@ -14,6 +14,9 @@
|
||||
#ifndef _TERMINAL__TYPES_H_
|
||||
#define _TERMINAL__TYPES_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/interface.h>
|
||||
|
||||
namespace Terminal {
|
||||
|
||||
struct Character;
|
||||
@ -84,7 +87,7 @@ struct Terminal::Position
|
||||
};
|
||||
|
||||
|
||||
struct Terminal::Character_array
|
||||
struct Terminal::Character_array : Genode::Interface
|
||||
{
|
||||
/**
|
||||
* Assign character to specified position
|
||||
|
Loading…
Reference in New Issue
Block a user