genode/repos/gems/src/server/terminal/session.h

219 lines
5.2 KiB
C++

/*
* \brief Terminal session interface
* \author Norman Feske
* \date 2018-02-06
*/
/*
* Copyright (C) 2018-2019 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 <util/utf8.h>
#include <root/component.h>
#include <terminal_session/terminal_session.h>
#include <terminal/read_buffer.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 _terminal_size { };
Size _reported_terminal_size { };
Attached_ram_dataspace _io_buffer;
Signal_context_capability _size_changed_sigh { };
Terminal::Position _last_cursor_pos { };
public:
/**
* Constructor
*/
Session_component(Env &env,
Read_buffer &read_buffer,
Character_consumer &character_consumer,
size_t io_buffer_size)
:
_read_buffer(read_buffer),
_character_consumer(character_consumer),
_io_buffer(env.ram(), env.rm(), io_buffer_size)
{ }
void notify_resized(Size new_size)
{
_terminal_size = new_size;
bool const client_is_out_of_date =
(_reported_terminal_size.columns() != new_size.columns()) ||
(_reported_terminal_size.lines() != new_size.lines());
if (client_is_out_of_date && _size_changed_sigh.valid())
Signal_transmitter(_size_changed_sigh).submit();
}
/********************************
** Terminal session interface **
********************************/
Size size() override
{
_reported_terminal_size = _terminal_size;
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)
{
char const *src = _io_buffer.local_addr<char const>();
unsigned const max = min(num_bytes, _io_buffer.size());
unsigned i = 0;
for (Utf8_ptr utf8(src); utf8.complete() && i < max; ) {
_character_consumer.consume_character(
Terminal::Character(utf8.codepoint()));
i += utf8.length();
if (i >= max)
break;
utf8 = Utf8_ptr(src + i);
}
/* consume trailing zero characters */
for (; i < max && src[i] == 0; i++);
/* we don't support UTF-8 sequences split into multiple writes */
if (i != num_bytes) {
warning("truncated UTF-8 sequence");
for (unsigned j = i; j < num_bytes; j++)
warning("(unhandled value ", Hex(src[j]), ")");
}
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);
}
void size_changed_sigh(Signal_context_capability cap) override
{
_size_changed_sigh = cap;
notify_resized(_terminal_size);
}
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 { };
Registry<Registered<Session_component> > _sessions { };
protected:
Session_component *_create_session(const char *) override
{
/*
* XXX read I/O buffer size from args
*/
size_t io_buffer_size = 4096;
Session_component &session = *new (md_alloc())
Registered<Session_component>(_sessions,
_env,
_read_buffer,
_character_consumer,
io_buffer_size);
/* propagate current terminal size to client */
session.notify_resized(_terminal_size);
return &session;
}
public:
/**
* Constructor
*/
Root_component(Env &env,
Allocator &md_alloc,
Read_buffer &read_buffer,
Character_consumer &character_consumer)
:
Genode::Root_component<Session_component>(env.ep(), md_alloc),
_env(env),
_read_buffer(read_buffer),
_character_consumer(character_consumer)
{ }
void notify_resized(Session::Size size)
{
_terminal_size = size;
_sessions.for_each([&] (Session_component &session) {
session.notify_resized(size); });
}
};
#endif /* _SESSION_H_ */