Input merger

This component merges the input events of multiple sources.

Example configuration:

<start name="input_merger">
	<resource name="RAM" quantum="1M" />
	<provides>
		<service name="Input" />
	</provides>
	<config>
		<input label="ps2" />
		<input label="usb_hid" />
	</config>
	<route>
		<service name="Input">
			<if-arg key="label" value="ps2" /> <child name="ps2_drv" />
		</service>
		<service name="Input">
			<if-arg key="label" value="usb_hid" /> <child name="usb_drv" />
		</service>
		<any-service> <parent /> <any-child /> </any-service>
	</route>
</start>

For each 'input' config node, the component opens an 'Input' session with the
configured label. This label is then evaluated by 'init' to route the session
request to a specific input source component.

Fixes #1259.
This commit is contained in:
Christian Prochaska 2014-10-01 16:00:52 +02:00 committed by Christian Helmuth
parent 3befb64afe
commit 81af714949
3 changed files with 199 additions and 0 deletions

View File

@ -0,0 +1,27 @@
This component merges the input events of multiple sources.
Example configuration:
<start name="input_merger">
<resource name="RAM" quantum="1M" />
<provides>
<service name="Input" />
</provides>
<config>
<input label="ps2" />
<input label="usb_hid" />
</config>
<route>
<service name="Input">
<if-arg key="label" value="ps2" /> <child name="ps2_drv" />
</service>
<service name="Input">
<if-arg key="label" value="usb_hid" /> <child name="usb_drv" />
</service>
<any-service> <parent /> <any-child /> </any-service>
</route>
</start>
For each 'input' config node, the component opens an 'Input' session with the
configured label. This label is then evaluated by 'init' to route the session
request to a specific input source component.

View File

@ -0,0 +1,169 @@
/*
* \brief Input event merger
* \author Christian Prochaska
* \date 2014-09-29
*/
/*
* Copyright (C) 2014 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <input/component.h>
#include <input_session/connection.h>
#include <os/attached_dataspace.h>
#include <os/config.h>
#include <os/server.h>
#include <os/static_root.h>
#include <util/list.h>
namespace Input_merger
{
using namespace Genode;
class Input_source : public List<Input_source>::Element
{
private:
Input::Session_capability _create_session(const char *label)
{
char session_args[sizeof(Parent::Session_args)];
Arg_string::set_arg(session_args, sizeof(Parent::Session_args),
"ram_quota", "16K");
Arg_string::set_arg(session_args, sizeof(Parent::Session_args),
"label", label);
return env()->parent()->session<Input::Session>(session_args);
}
public:
Input::Session_client session_client;
Attached_dataspace dataspace;
Input_source(const char *label)
: session_client(_create_session(label)),
dataspace(session_client.dataspace()) { }
~Input_source()
{
env()->parent()->close(session_client);
}
};
struct Main;
}
/******************
** Main program **
******************/
struct Input_merger::Main
{
Server::Entrypoint &ep;
List<Input_source> input_source_list;
/*
* Input session provided to our client
*/
Input::Session_component input_session_component;
/*
* Attach root interface to the entry point
*/
Static_root<Input::Session> input_root { ep.manage(input_session_component) };
void handle_input(unsigned)
{
for (Input_source *input_source = input_source_list.first();
input_source;
input_source = input_source->next()) {
Input::Event const * const events =
input_source->dataspace.local_addr<Input::Event>();
unsigned const num = input_source->session_client.flush();
for (unsigned i = 0; i < num; i++)
input_session_component.submit(events[i]);
}
}
Signal_rpc_member<Main> input_dispatcher =
{ ep, *this, &Main::handle_input};
void handle_config_update(unsigned)
{
Genode::config()->reload();
for (Input_source *input_source;
(input_source = input_source_list.first()); ) {
input_source_list.remove(input_source);
destroy(env()->heap(), input_source);
}
try {
for (Xml_node input_node = config()->xml_node().sub_node("input");
;
input_node = input_node.next("input")) {
char label_buf[128];
input_node.attribute("label").value(label_buf, sizeof(label_buf));
Input_source *input_source(new (env()->heap()) Input_source(label_buf));
input_source_list.insert(input_source);
input_source->session_client.sigh(input_dispatcher);
}
} catch (Xml_node::Nonexistent_sub_node) { }
}
Signal_rpc_member<Main> config_update_dispatcher =
{ ep, *this, &Main::handle_config_update};
/**
* Constructor
*/
Main(Server::Entrypoint &ep) : ep(ep)
{
input_session_component.event_queue().enabled(true);
/*
* Apply initial configuration
*/
handle_config_update(0);
/*
* Register signal handler
*/
Genode::config()->sigh(config_update_dispatcher);
/*
* Announce service
*/
Genode::env()->parent()->announce(ep.manage(input_root));
}
};
/************
** Server **
************/
namespace Server {
char const *name() { return "input_merger_ep"; }
size_t stack_size() { return 4*1024*sizeof(addr_t); }
void construct(Entrypoint &ep) { static Input_merger::Main inst(ep); }
}

View File

@ -0,0 +1,3 @@
TARGET = input_merger
SRC_CC = main.cc
LIBS = base config server