server/terminal: API transition

The key repeat and flush handling was added by Norman Feske.

Ref #1987
This commit is contained in:
Emery Hemingway 2017-01-02 16:39:55 +01:00 committed by Norman Feske
parent 5e75ac4f87
commit 07cb4b2a4e
2 changed files with 208 additions and 117 deletions

View File

@ -12,19 +12,19 @@
*/
/* Genode includes */
#include <base/env.h>
#include <base/component.h>
#include <base/log.h>
#include <base/heap.h>
#include <framebuffer_session/connection.h>
#include <input_session/connection.h>
#include <timer_session/connection.h>
#include <cap_session/connection.h>
#include <root/component.h>
#include <os/attached_ram_dataspace.h>
#include <base/attached_rom_dataspace.h>
#include <base/attached_ram_dataspace.h>
#include <input/event.h>
#include <os/config.h>
#include <util/color.h>
#include <os/pixel_rgb565.h>
#include <os/timer.h>
/* terminal includes */
#include <terminal/decoder.h>
@ -37,6 +37,11 @@
/* nitpicker graphic back end */
#include <nitpicker_gfx/text_painter.h>
namespace Terminal {
using namespace Genode;
struct Main;
}
using Genode::Pixel_rgb565;
typedef Text_painter::Font Font;
@ -264,15 +269,22 @@ namespace Terminal {
};
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;
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;
@ -295,7 +307,7 @@ namespace Terminal {
Terminal::Position _last_cursor_pos;
Font_family const *_font_family;
Font_family const &_font_family;
/**
* Initialize framebuffer-related attributes
@ -307,7 +319,7 @@ namespace Terminal {
return Genode::Dataspace_capability();
}
return _framebuffer->dataspace();
return _framebuffer.dataspace();
}
public:
@ -315,16 +327,20 @@ namespace Terminal {
/**
* Constructor
*/
Session_component(Read_buffer *read_buffer,
Framebuffer::Session *framebuffer,
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),
_io_buffer(Genode::env()->ram_session(), io_buffer_size),
_fb_mode(_framebuffer->mode()),
_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 */
@ -335,12 +351,12 @@ namespace Terminal {
_columns(_fb_mode.width()/_char_width),
_lines(_fb_mode.height()/_char_height),
_fb_addr(Genode::env()->rm_session()->attach(_fb_ds_cap)),
_char_cell_array(_columns, _lines, Genode::env()->heap()),
_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)
_font_family(font_family)
{
using namespace Genode;
@ -349,7 +365,7 @@ namespace Terminal {
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());
framebuffer.refresh(0, 0, _fb_mode.width(), _fb_mode.height());
_flush_callback_registry.add(this);
}
@ -367,7 +383,7 @@ namespace Terminal {
(Pixel_rgb565 *)_fb_addr,
_fb_mode.width(),
_fb_mode.height(),
*_font_family);
_font_family);
int first_dirty_line = 10000,
@ -384,9 +400,9 @@ namespace Terminal {
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);
_framebuffer.refresh(0, first_dirty_line*_char_height,
_fb_mode.width(),
num_dirty_lines*_char_height);
}
@ -396,7 +412,7 @@ namespace Terminal {
Size size() { return Size(_columns, _lines); }
bool avail() { return !_read_buffer->empty(); }
bool avail() { return !_read_buffer.empty(); }
Genode::size_t _read(Genode::size_t dst_len)
{
@ -404,9 +420,9 @@ namespace Terminal {
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);
do {
dst[num_bytes++] = _read_buffer->get();
} while (!_read_buffer->empty() && num_bytes < dst_size);
while (!_read_buffer.empty() && num_bytes < dst_size)
dst[num_bytes++] = _read_buffer.get();
return num_bytes;
}
@ -422,6 +438,7 @@ namespace Terminal {
/* submit character to sequence decoder */
_decoder.insert(src[i]);
}
_trigger_flush_callback.trigger_flush();
}
Genode::Dataspace_capability _dataspace()
@ -441,7 +458,7 @@ namespace Terminal {
void read_avail_sigh(Genode::Signal_context_capability cap)
{
_read_buffer->sigh(cap);
_read_buffer.sigh(cap);
}
Genode::size_t read(void *buf, Genode::size_t) { return 0; }
@ -453,9 +470,11 @@ namespace Terminal {
{
private:
Read_buffer *_read_buffer;
Framebuffer::Session *_framebuffer;
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:
@ -470,10 +489,12 @@ namespace Terminal {
Genode::size_t io_buffer_size = 4096;
Session_component *session =
new (md_alloc()) Session_component(_read_buffer,
new (md_alloc()) Session_component(_env, *md_alloc(),
_read_buffer,
_framebuffer,
io_buffer_size,
_flush_callback_registry,
_trigger_flush_callback,
_font_family);
return session;
}
@ -483,41 +504,175 @@ namespace Terminal {
/**
* Constructor
*/
Root_component(Genode::Rpc_entrypoint *ep,
Genode::Allocator *md_alloc,
Read_buffer *read_buffer,
Framebuffer::Session *framebuffer,
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>(ep, md_alloc),
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)
{ }
};
}
int main(int, char **)
struct Terminal::Main
{
Genode::Env &_env;
Framebuffer::Connection _framebuffer { _env, Framebuffer::Mode() };
Input::Connection _input { _env };
Timer::Connection _timer_conection { _env };
Genode::Timer _timer { _timer_conection, _env.ep() };
Sliced_heap _sliced_heap { _env.ram(), _env.rm() };
/* input read buffer */
Read_buffer _read_buffer;
Terminal::Flush_callback_registry _flush_callback_registry;
/* create root interface for service */
Terminal::Root_component _root;
Terminal::Scancode_tracker _scancode_tracker;
/* state needed for key-repeat handling */
unsigned const _repeat_delay = 250;
unsigned const _repeat_rate = 25;
unsigned _repeat_next = 0;
void _handle_input();
Signal_handler<Main> _input_handler {
_env.ep(), *this, &Main::_handle_input };
void _handle_key_repeat(Time_source::Microseconds);
One_shot_timeout<Main> _key_repeat_timeout {
_timer, *this, &Main::_handle_key_repeat };
void _handle_flush(Time_source::Microseconds);
/*
* 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()
{
if (!_flush_scheduled) {
_flush_timeout.start(Time_source::Microseconds{1000*_flush_delay});
_flush_scheduled = true;
}
}
/*
* 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 };
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, _sliced_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 */
_env.parent().announce(_env.ep().manage(_root));
}
};
void Terminal::Main::_handle_input()
{
_input.for_each_event([&] (Input::Event const &event) {
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);
if (press) {
_scancode_tracker.emit_current_character(_read_buffer);
/* setup first key repeat */
_repeat_next = _repeat_delay;
}
if (release)
_repeat_next = 0;
});
if (_repeat_next)
_key_repeat_timeout.start(Time_source::Microseconds{1000*_repeat_next});
}
void Terminal::Main::_handle_key_repeat(Time_source::Microseconds)
{
if (_repeat_next) {
/* repeat current character or sequence */
_scancode_tracker.emit_current_character(_read_buffer);
_repeat_next = _repeat_rate;
}
_handle_input();
}
void Terminal::Main::_handle_flush(Time_source::Microseconds)
{
_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)
{
using namespace Genode;
log("--- terminal service started ---");
static Framebuffer::Connection framebuffer;
static Input::Connection input;
static Timer::Connection timer;
static Cap_connection cap;
/* initialize entry point that serves the root interface */
enum { STACK_SIZE = 2*sizeof(addr_t)*1024 };
static Rpc_entrypoint ep(&cap, STACK_SIZE, "terminal_ep");
static Sliced_heap sliced_heap(env()->ram_session(), env()->rm_session());
/* input read buffer */
static Terminal::Read_buffer read_buffer;
Attached_rom_dataspace config(env, "config");
/* initialize color palette */
color_palette[0] = Color( 0, 0, 0); /* black */
@ -536,17 +691,11 @@ int main(int, char **)
color_palette[i + 8] = col;
}
/* built-in fonts */
extern char _binary_notix_8_tff_start;
extern char _binary_terminus_12_tff_start;
extern char _binary_terminus_16_tff_start;
/* pick font according to config file */
char const *font_data = &_binary_terminus_16_tff_start;
try {
size_t font_size = 16;
config()->xml_node().sub_node("font")
.attribute("size").value(&font_size);
config.xml().sub_node("font").attribute("size").value(&font_size);
switch (font_size) {
case 8: font_data = &_binary_notix_8_tff_start; break;
@ -562,22 +711,6 @@ int main(int, char **)
log("cell size is ", (int)font_family.cell_width(),
"x", (int)font_family.cell_height());
static Terminal::Flush_callback_registry flush_callback_registry;
/* create root interface for service */
static Terminal::Root_component root(&ep, &sliced_heap,
&read_buffer, &framebuffer,
flush_callback_registry,
font_family);
/* announce service at our parent */
env()->parent()->announce(ep.manage(&root));
/* state needed for key-repeat handling */
static int const repeat_delay = 170;
static int const repeat_rate = 25;
static int repeat_cnt = 0;
unsigned char *keymap = Terminal::usenglish_keymap;
unsigned char *shift = Terminal::usenglish_shift;
unsigned char *altgr = 0;
@ -586,7 +719,7 @@ int main(int, char **)
* Read keyboard layout from config file
*/
try {
if (config()->xml_node().sub_node("keyboard")
if (config.xml().sub_node("keyboard")
.attribute("layout").has_value("de")) {
keymap = Terminal::german_keymap;
@ -595,47 +728,5 @@ int main(int, char **)
}
} catch (...) { }
static Terminal::Scancode_tracker
scancode_tracker(keymap, shift, altgr, Terminal::control);
while (1) {
flush_callback_registry.flush();
while (!input.pending()) {
enum { PASSED_MSECS = 10 };
timer.msleep(PASSED_MSECS);
flush_callback_registry.flush();
if (scancode_tracker.valid()) {
repeat_cnt -= PASSED_MSECS;
if (repeat_cnt < 0) {
/* repeat current character or sequence */
scancode_tracker.emit_current_character(read_buffer);
/* reset repeat counter according to repeat rate */
repeat_cnt = repeat_rate;
}
}
}
input.for_each_event([&] (Input::Event const &event) {
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);
if (press)
scancode_tracker.emit_current_character(read_buffer);
/* setup first key repeat */
repeat_cnt = repeat_delay;
});
}
return 0;
static Terminal::Main main(env, font_family, keymap, shift, altgr, Terminal::control);
}

View File

@ -1,4 +1,4 @@
TARGET = terminal
SRC_CC = main.cc
LIBS = base config
LIBS = base timeout
SRC_BIN = $(notdir $(wildcard $(PRG_DIR)/*.tff))