mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-07 11:27:29 +00:00
parent
02d07655ce
commit
59014a50f1
105
repos/os/run/clipboard.run
Normal file
105
repos/os/run/clipboard.run
Normal file
@ -0,0 +1,105 @@
|
||||
#
|
||||
# Build
|
||||
#
|
||||
|
||||
set build_components {
|
||||
core init drivers/timer
|
||||
server/clipboard server/report_rom test/clipboard drivers/timer
|
||||
}
|
||||
|
||||
build $build_components
|
||||
|
||||
create_boot_directory
|
||||
|
||||
#
|
||||
# Generate config
|
||||
#
|
||||
|
||||
append config {
|
||||
<config>
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="RAM"/>
|
||||
<service name="RM"/>
|
||||
<service name="LOG"/>
|
||||
<service name="IRQ"/>
|
||||
<service name="IO_MEM"/>
|
||||
<service name="IO_PORT"/>
|
||||
<service name="CAP"/>
|
||||
<service name="SIGNAL"/>
|
||||
</parent-provides>
|
||||
|
||||
<default-route>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</default-route>
|
||||
|
||||
<start name="timer">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Timer"/></provides>
|
||||
</start>
|
||||
|
||||
<start name="report_rom">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides> <service name="Report"/> <service name="ROM"/> </provides>
|
||||
<config verbose="yes">
|
||||
<rom>
|
||||
<policy label="clipboard -> focus" report="test-clipboard -> focus"/>
|
||||
</rom>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="clipboard">
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<provides>
|
||||
<service name="ROM"/>
|
||||
<service name="Report"/>
|
||||
</provides>
|
||||
<config verbose="yes">
|
||||
|
||||
<flow from="hobby" to="work" />
|
||||
<flow from="hobby" to="admin" />
|
||||
<flow from="work" to="admin" />
|
||||
|
||||
<policy label="test-clipboard -> win7" domain="work" />
|
||||
<policy label="test-clipboard -> linux" domain="hobby" />
|
||||
<policy label="test-clipboard -> noux" domain="admin" />
|
||||
|
||||
</config>
|
||||
<route>
|
||||
<any-service> <child name="report_rom"/> <parent/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="test-clipboard">
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<route>
|
||||
<!-- for the simulation of nitpicker's focus reports -->
|
||||
<service name="Report">
|
||||
<if-arg key="label" value="focus"/>
|
||||
<child name="report_rom" />
|
||||
</service>
|
||||
|
||||
<!-- for the simulation of clipboard clients -->
|
||||
<any-service>
|
||||
<child name="clipboard"/> <any-child/> <parent/>
|
||||
</any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
</config>}
|
||||
|
||||
install_config $config
|
||||
|
||||
#
|
||||
# Boot modules
|
||||
#
|
||||
|
||||
set boot_modules { core init timer report_rom clipboard test-clipboard }
|
||||
|
||||
build_boot_image $boot_modules
|
||||
|
||||
append qemu_args " -nographic "
|
||||
|
||||
run_genode_until {.*-- state WAIT_FOR_SUCCESS --.*\n} 40
|
||||
|
||||
|
4
repos/os/src/server/clipboard/README
Normal file
4
repos/os/src/server/clipboard/README
Normal file
@ -0,0 +1,4 @@
|
||||
The "clipboard" component is both a report service and a ROM service. The
|
||||
clients of the report service can issue new clipboard content, which is then
|
||||
propagated to the clients of the ROM service according to a configurable
|
||||
information-flow policy.
|
219
repos/os/src/server/clipboard/main.cc
Normal file
219
repos/os/src/server/clipboard/main.cc
Normal file
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* \brief Clipboard used for copy and paste between domains
|
||||
* \author Norman Feske
|
||||
* \date 2015-09-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 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 <base/heap.h>
|
||||
#include <base/env.h>
|
||||
#include <os/server.h>
|
||||
#include <os/config.h>
|
||||
#include <os/attached_rom_dataspace.h>
|
||||
#include <report_rom/rom_service.h>
|
||||
#include <report_rom/report_service.h>
|
||||
|
||||
namespace Server { struct Main; }
|
||||
|
||||
|
||||
/**
|
||||
* The clipboard uses a single ROM module for all clients
|
||||
*/
|
||||
struct Rom::Registry : Rom::Registry_for_reader, Rom::Registry_for_writer
|
||||
{
|
||||
Module module;
|
||||
|
||||
/**
|
||||
* Rom::Registry_for_writer interface
|
||||
*/
|
||||
Module &lookup(Writer &, Module::Name const &) override { return module; }
|
||||
void release(Writer &, Module &) override { }
|
||||
|
||||
/**
|
||||
* Rom::Registry_for_reader interface
|
||||
*/
|
||||
Module &lookup(Reader &reader, Module::Name const &) override
|
||||
{
|
||||
module._register(reader);
|
||||
return module;
|
||||
}
|
||||
|
||||
void release(Reader &reader, Readable_module &) override
|
||||
{
|
||||
module._unregister(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Registry(Module::Read_policy const &read_policy,
|
||||
Module::Write_policy const &write_policy)
|
||||
:
|
||||
module("clipboard", read_policy, write_policy)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
struct Server::Main : Rom::Module::Read_policy, Rom::Module::Write_policy
|
||||
{
|
||||
Entrypoint &_ep;
|
||||
|
||||
Genode::Sliced_heap _sliced_heap = { Genode::env()->ram_session(),
|
||||
Genode::env()->rm_session() };
|
||||
bool _verbose_config()
|
||||
{
|
||||
char const *attr = "verbose";
|
||||
return Genode::config()->xml_node().has_attribute(attr)
|
||||
&& Genode::config()->xml_node().attribute(attr).has_value("yes");
|
||||
}
|
||||
|
||||
bool verbose = _verbose_config();
|
||||
|
||||
typedef Genode::String<100> Domain;
|
||||
|
||||
Genode::Attached_rom_dataspace _focus_ds { "focus" };
|
||||
|
||||
Genode::Signal_rpc_member<Main> _focus_dispatcher =
|
||||
{ _ep, *this, &Main::_handle_focus };
|
||||
|
||||
Domain _focused_domain;
|
||||
|
||||
/**
|
||||
* Handle the change of the current nitpicker focus
|
||||
*
|
||||
* We only accept reports from the currently focused domain.
|
||||
*/
|
||||
void _handle_focus(unsigned)
|
||||
{
|
||||
_focus_ds.update();
|
||||
|
||||
_focused_domain = Domain();
|
||||
|
||||
try {
|
||||
Genode::Xml_node focus(_focus_ds.local_addr<char>(), _focus_ds.size());
|
||||
|
||||
if (focus.attribute("active").has_value("yes"))
|
||||
_focused_domain = focus.attribute_value("domain", Domain());
|
||||
|
||||
} catch (...) { }
|
||||
}
|
||||
|
||||
Domain _domain(Genode::Session_label const &label) const
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
try {
|
||||
return Session_policy(label).attribute_value("domain", Domain());
|
||||
} catch (Session_policy::No_policy_defined) { }
|
||||
|
||||
return Domain();
|
||||
}
|
||||
|
||||
Domain _domain(Rom::Reader const &reader) const
|
||||
{
|
||||
Rom::Session_component const &rom_session =
|
||||
static_cast<Rom::Session_component const &>(reader);
|
||||
|
||||
return _domain(rom_session.label());
|
||||
}
|
||||
|
||||
Domain _domain(Rom::Writer const &writer) const
|
||||
{
|
||||
Report::Session_component const &report_session =
|
||||
static_cast<Report::Session_component const &>(writer);
|
||||
|
||||
return _domain(report_session.label());
|
||||
}
|
||||
|
||||
bool _flow_defined(Domain const &from, Domain const &to) const
|
||||
{
|
||||
if (!from.valid() || !to.valid())
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Search config for flow node with matching 'from' and 'to'
|
||||
* attributes.
|
||||
*/
|
||||
bool result = false;
|
||||
try {
|
||||
|
||||
auto match_flow = [&] (Genode::Xml_node flow) {
|
||||
if (flow.attribute_value("from", Domain()) == from
|
||||
&& flow.attribute_value("to", Domain()) == to)
|
||||
result = true; };
|
||||
|
||||
Genode::config()->xml_node().for_each_sub_node("flow", match_flow);
|
||||
|
||||
} catch (Genode::Xml_node::Nonexistent_sub_node) { }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rom::Module::Read_policy interface
|
||||
*/
|
||||
bool read_permitted(Rom::Module const &module,
|
||||
Rom::Writer const &writer,
|
||||
Rom::Reader const &reader) const override
|
||||
{
|
||||
Domain const from_domain = _domain(writer);
|
||||
Domain const to_domain = _domain(reader);
|
||||
|
||||
if (from_domain == to_domain)
|
||||
return true;
|
||||
|
||||
if (_flow_defined(from_domain, to_domain))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rom::Module::Write_policy interface
|
||||
*/
|
||||
bool write_permitted(Rom::Module const &module,
|
||||
Rom::Writer const &writer) const override
|
||||
{
|
||||
if (_focused_domain.valid() && _domain(writer) == _focused_domain)
|
||||
return true;
|
||||
|
||||
PWRN("unexpected attempt by '%s' to write to '%s'",
|
||||
writer.label().string(), module.name().string());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Rom::Registry _rom_registry { *this, *this };
|
||||
|
||||
Report::Root report_root = { _ep, _sliced_heap, _rom_registry, verbose };
|
||||
Rom ::Root rom_root = { _ep, _sliced_heap, _rom_registry };
|
||||
|
||||
Main(Entrypoint &ep) : _ep(ep)
|
||||
{
|
||||
_focus_ds.sigh(_focus_dispatcher);
|
||||
|
||||
Genode::env()->parent()->announce(_ep.manage(report_root));
|
||||
Genode::env()->parent()->announce(_ep.manage(rom_root));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace Server {
|
||||
|
||||
char const *name() { return "report_rom_ep"; }
|
||||
|
||||
size_t stack_size() { return 4*1024*sizeof(long); }
|
||||
|
||||
void construct(Entrypoint &ep)
|
||||
{
|
||||
static Main main(ep);
|
||||
}
|
||||
}
|
4
repos/os/src/server/clipboard/target.mk
Normal file
4
repos/os/src/server/clipboard/target.mk
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = clipboard
|
||||
SRC_CC = main.cc
|
||||
LIBS = base server config
|
||||
INC_DIR += $(PRG_DIR)
|
429
repos/os/src/test/clipboard/main.cc
Normal file
429
repos/os/src/test/clipboard/main.cc
Normal file
@ -0,0 +1,429 @@
|
||||
/*
|
||||
* \brief Clipboard test
|
||||
* \author Norman Feske
|
||||
* \date 2015-09-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 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 <os/server.h>
|
||||
#include <report_session/connection.h>
|
||||
#include <os/attached_rom_dataspace.h>
|
||||
#include <os/reporter.h>
|
||||
#include <util/xml_generator.h>
|
||||
#include <util/xml_node.h>
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
|
||||
class Nitpicker
|
||||
{
|
||||
private:
|
||||
|
||||
Timer::Session &_timer;
|
||||
|
||||
Genode::Reporter _focus_reporter { "focus" };
|
||||
|
||||
void _focus(char const *domain, bool active)
|
||||
{
|
||||
Genode::Reporter::Xml_generator xml(_focus_reporter, [&] () {
|
||||
xml.attribute("domain", domain);
|
||||
xml.attribute("active", active ? "yes" : "no");
|
||||
});
|
||||
|
||||
/*
|
||||
* Trigger a state change after a while. We wait a bit after
|
||||
* reporting a new focus to give the new state some time to
|
||||
* propagate through the report-rom server to the clipboard.
|
||||
*/
|
||||
_timer.trigger_once(250*1000);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Nitpicker(Timer::Session &timer)
|
||||
:
|
||||
_timer(timer)
|
||||
{
|
||||
_focus_reporter.enabled(true);
|
||||
}
|
||||
|
||||
void focus_active (char const *domain) { _focus(domain, true); }
|
||||
void focus_inactive(char const *domain) { _focus(domain, false); }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Callback called each time when a subsystem makes progress
|
||||
*
|
||||
* This function drives the state machine of the test program.
|
||||
*/
|
||||
struct Handle_step_fn
|
||||
{
|
||||
virtual void handle_step(unsigned) = 0;
|
||||
};
|
||||
|
||||
|
||||
class Subsystem
|
||||
{
|
||||
private:
|
||||
|
||||
Server::Entrypoint &_ep;
|
||||
|
||||
typedef Genode::String<100> Label;
|
||||
|
||||
Label _name;
|
||||
|
||||
Handle_step_fn &_handle_step_fn;
|
||||
|
||||
bool _expect_import = true;
|
||||
|
||||
Label _session_label()
|
||||
{
|
||||
char buf[Label::capacity()];
|
||||
Genode::snprintf(buf, sizeof(buf), "%s -> clipboard", _name.string());
|
||||
return Label(buf);
|
||||
}
|
||||
|
||||
Genode::Attached_rom_dataspace _import_rom;
|
||||
|
||||
char const *_import_content = nullptr;
|
||||
|
||||
Report::Connection _export_report { _session_label().string() };
|
||||
|
||||
Genode::Attached_dataspace _export_report_ds { _export_report.dataspace() };
|
||||
|
||||
static void _log_lines(char const *string, Genode::size_t len)
|
||||
{
|
||||
Genode::print_lines<200>(string, len,
|
||||
[&] (char const *line) { PLOG(" %s", line); });
|
||||
}
|
||||
|
||||
void _handle_import(unsigned)
|
||||
{
|
||||
if (!_expect_import) {
|
||||
class Unexpected_clipboard_import { };
|
||||
throw Unexpected_clipboard_import();
|
||||
}
|
||||
|
||||
PLOG("\n%s: import new content:", _name.string());
|
||||
|
||||
_import_rom.update();
|
||||
_import_content = _import_rom.local_addr<char>();
|
||||
_log_lines(_import_content, _import_rom.size());
|
||||
|
||||
/* trigger next step */
|
||||
_handle_step_fn.handle_step(0);
|
||||
}
|
||||
|
||||
Genode::Signal_rpc_member<Subsystem> _import_dispatcher =
|
||||
{ _ep, *this, &Subsystem::_handle_import };
|
||||
|
||||
static void _strip_outer_whitespace(char const **str_ptr, Genode::size_t &len)
|
||||
{
|
||||
char const *str = *str_ptr;
|
||||
|
||||
/* strip leading whitespace */
|
||||
for (; Genode::is_whitespace(*str); str++, len--);
|
||||
|
||||
/* strip trailing whitespace */
|
||||
for (; len > 1 && Genode::is_whitespace(str[len - 1]); len--);
|
||||
|
||||
*str_ptr = str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return currently present imported text
|
||||
*
|
||||
* \throw Xml_node::Nonexistent_sub_node
|
||||
*/
|
||||
Genode::Xml_node _imported_text() const
|
||||
{
|
||||
if (!_import_content)
|
||||
throw Genode::Xml_node::Nonexistent_sub_node();
|
||||
|
||||
Genode::Xml_node clipboard(_import_content,
|
||||
_import_rom.size());
|
||||
|
||||
return clipboard.sub_node("text");
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Subsystem(Server::Entrypoint &ep, char const *name,
|
||||
Handle_step_fn &handle_step_fn)
|
||||
:
|
||||
_ep(ep),
|
||||
_name(name),
|
||||
_handle_step_fn(handle_step_fn),
|
||||
_import_rom(_session_label().string())
|
||||
{
|
||||
_import_rom.sigh(_import_dispatcher);
|
||||
}
|
||||
|
||||
void copy(char const *str)
|
||||
{
|
||||
Genode::Xml_generator xml(_export_report_ds.local_addr<char>(),
|
||||
_export_report_ds.size(),
|
||||
"clipboard", [&] ()
|
||||
{
|
||||
xml.attribute("origin", _name.string());
|
||||
xml.node("text", [&] () {
|
||||
xml.append(str, Genode::strlen(str));
|
||||
});
|
||||
});
|
||||
|
||||
PLOG("\n%s: export content:", _name.string());
|
||||
_log_lines(_export_report_ds.local_addr<char>(), xml.used());
|
||||
|
||||
_export_report.submit(xml.used());
|
||||
}
|
||||
|
||||
bool has_content(char const *str) const
|
||||
{
|
||||
using namespace Genode;
|
||||
try {
|
||||
typedef Genode::String<100> String;
|
||||
|
||||
String const expected(str);
|
||||
String const imported = _imported_text().decoded_content<String>();
|
||||
|
||||
return expected == imported;
|
||||
}
|
||||
catch (...) { }
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_cleared() const
|
||||
{
|
||||
try {
|
||||
_imported_text();
|
||||
return false;
|
||||
} catch (...) { }
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure assertion for situation where no imports are expected
|
||||
*/
|
||||
void expect_import(bool expect) { _expect_import = expect; }
|
||||
};
|
||||
|
||||
|
||||
namespace Server { struct Main; }
|
||||
|
||||
|
||||
struct Server::Main : Handle_step_fn
|
||||
{
|
||||
Entrypoint &_ep;
|
||||
|
||||
enum State {
|
||||
INIT,
|
||||
FOCUSED_HOBBY_DOMAIN,
|
||||
EXPECT_CAT_PICTURE,
|
||||
FOCUSED_ADMIN_DOMAIN,
|
||||
EXPECT_PRIVATE_KEY,
|
||||
BLOCKED_REPETITION,
|
||||
FOCUSED_WORK_DOMAIN,
|
||||
EXPECT_CONTRACT,
|
||||
FOCUS_BECOMES_INACTIVE,
|
||||
BLOCKED_WHEN_INACTIVE,
|
||||
FOCUSED_HOBBY_DOMAIN_AGAIN,
|
||||
WAIT_FOR_SUCCESS
|
||||
};
|
||||
|
||||
State _state = INIT;
|
||||
|
||||
static char const *_state_name(State state)
|
||||
{
|
||||
switch (state) {
|
||||
case INIT: return "INIT";
|
||||
case FOCUSED_HOBBY_DOMAIN: return "FOCUSED_HOBBY_DOMAIN";
|
||||
case EXPECT_CAT_PICTURE: return "EXPECT_CAT_PICTURE";
|
||||
case FOCUSED_ADMIN_DOMAIN: return "FOCUSED_ADMIN_DOMAIN";
|
||||
case EXPECT_PRIVATE_KEY: return "EXPECT_PRIVATE_KEY";
|
||||
case BLOCKED_REPETITION: return "BLOCKED_REPETITION";
|
||||
case FOCUSED_WORK_DOMAIN: return "FOCUSED_WORK_DOMAIN";
|
||||
case EXPECT_CONTRACT: return "EXPECT_CONTRACT";
|
||||
case FOCUS_BECOMES_INACTIVE: return "FOCUS_BECOMES_INACTIVE";
|
||||
case BLOCKED_WHEN_INACTIVE: return "BLOCKED_WHEN_INACTIVE";
|
||||
case FOCUSED_HOBBY_DOMAIN_AGAIN: return "FOCUSED_HOBBY_DOMAIN_AGAIN";
|
||||
case WAIT_FOR_SUCCESS: return "WAIT_FOR_SUCCESS";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void _enter_state(State state)
|
||||
{
|
||||
PINF("\n-> entering state %s", _state_name(state));
|
||||
_state = state;
|
||||
}
|
||||
|
||||
void handle_step(unsigned cnt) override
|
||||
{
|
||||
PLOG("\n -- state %s --", _state_name(_state));
|
||||
|
||||
char const * const cat_picture = "cat picture";
|
||||
char const * const private_key = "private key";
|
||||
char const * const another_private_key = "another private key";
|
||||
char const * const contract = "contract";
|
||||
char const * const garbage = "garbage";
|
||||
|
||||
char const * const hobby_domain = "hobby";
|
||||
char const * const work_domain = "work";
|
||||
char const * const admin_domain = "admin";
|
||||
|
||||
switch (_state) {
|
||||
|
||||
case INIT:
|
||||
_nitpicker.focus_active(hobby_domain);
|
||||
_enter_state(FOCUSED_HOBBY_DOMAIN);
|
||||
return;
|
||||
|
||||
case FOCUSED_HOBBY_DOMAIN:
|
||||
_hobby.copy(cat_picture);
|
||||
_enter_state(EXPECT_CAT_PICTURE);
|
||||
return;
|
||||
|
||||
case EXPECT_CAT_PICTURE:
|
||||
|
||||
if (!_hobby.has_content(cat_picture)
|
||||
|| !_work .has_content(cat_picture)
|
||||
|| !_admin.has_content(cat_picture))
|
||||
return;
|
||||
|
||||
_nitpicker.focus_active(admin_domain);
|
||||
_enter_state(FOCUSED_ADMIN_DOMAIN);
|
||||
return;
|
||||
|
||||
case FOCUSED_ADMIN_DOMAIN:
|
||||
_admin.copy(private_key);
|
||||
_enter_state(EXPECT_PRIVATE_KEY);
|
||||
return;
|
||||
|
||||
case EXPECT_PRIVATE_KEY:
|
||||
|
||||
if (!_hobby.is_cleared()
|
||||
|| !_work .is_cleared()
|
||||
|| !_admin.has_content(private_key))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Issue a copy operation that leaves the hobby and work
|
||||
* domains unchanged. The unchanged domains are not expected
|
||||
* to receive any notification. Otherwise, such notifications
|
||||
* could be misused as a covert channel.
|
||||
*/
|
||||
_work .expect_import(false);
|
||||
_hobby.expect_import(false);
|
||||
_admin.copy(another_private_key);
|
||||
|
||||
_timer.trigger_once(500*1000);
|
||||
_enter_state(BLOCKED_REPETITION);
|
||||
return;
|
||||
|
||||
case BLOCKED_REPETITION:
|
||||
|
||||
/*
|
||||
* Let the work and hobby domains accept new imports.
|
||||
*/
|
||||
_work .expect_import(true);
|
||||
_hobby.expect_import(true);
|
||||
|
||||
_nitpicker.focus_active(work_domain);
|
||||
_enter_state(FOCUSED_WORK_DOMAIN);
|
||||
return;
|
||||
|
||||
case FOCUSED_WORK_DOMAIN:
|
||||
_work.copy(contract);
|
||||
_enter_state(EXPECT_CONTRACT);
|
||||
return;
|
||||
|
||||
case EXPECT_CONTRACT:
|
||||
|
||||
if (!_hobby.is_cleared()
|
||||
|| !_work .has_content(contract)
|
||||
|| !_admin.has_content(contract))
|
||||
return;
|
||||
|
||||
_nitpicker.focus_inactive(work_domain);
|
||||
_enter_state(FOCUS_BECOMES_INACTIVE);
|
||||
return;
|
||||
|
||||
case FOCUS_BECOMES_INACTIVE:
|
||||
|
||||
/*
|
||||
* With the focus becoming inactive, we do not expect the
|
||||
* delivery of any new clipboard content.
|
||||
*/
|
||||
_work .expect_import(false);
|
||||
_admin.expect_import(false);
|
||||
_hobby.expect_import(false);
|
||||
_work.copy(garbage);
|
||||
|
||||
/*
|
||||
* Since no state changes are triggered from the outside,
|
||||
* we schedule a timeout to proceed.
|
||||
*/
|
||||
_timer.trigger_once(500*1000);
|
||||
_enter_state(BLOCKED_WHEN_INACTIVE);
|
||||
return;
|
||||
|
||||
case BLOCKED_WHEN_INACTIVE:
|
||||
_nitpicker.focus_active(hobby_domain);
|
||||
_enter_state(FOCUSED_HOBBY_DOMAIN_AGAIN);
|
||||
return;
|
||||
|
||||
case FOCUSED_HOBBY_DOMAIN_AGAIN:
|
||||
/*
|
||||
* Let the work domain try to issue a copy operation while the
|
||||
* hobby domain is focused. The clipboard is expected to block
|
||||
* this report.
|
||||
*/
|
||||
_work.copy(garbage);
|
||||
_timer.trigger_once(500*1000);
|
||||
_enter_state(WAIT_FOR_SUCCESS);
|
||||
return;
|
||||
|
||||
case WAIT_FOR_SUCCESS:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Genode::Signal_rpc_member<Main> _step_dispatcher =
|
||||
{ _ep, *this, &Main::handle_step };
|
||||
|
||||
Subsystem _admin { _ep, "noux", *this };
|
||||
Subsystem _hobby { _ep, "linux", *this };
|
||||
Subsystem _work { _ep, "win7", *this };
|
||||
|
||||
Timer::Connection _timer;
|
||||
|
||||
Nitpicker _nitpicker { _timer };
|
||||
|
||||
Main(Entrypoint &ep) : _ep(ep)
|
||||
{
|
||||
_timer.sigh(_step_dispatcher);
|
||||
|
||||
/* trigger first step */
|
||||
handle_step(0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace Server {
|
||||
|
||||
char const *name() { return "ep"; }
|
||||
|
||||
size_t stack_size() { return 4*1024*sizeof(long); }
|
||||
|
||||
void construct(Entrypoint &ep)
|
||||
{
|
||||
static Main main(ep);
|
||||
}
|
||||
}
|
3
repos/os/src/test/clipboard/target.mk
Normal file
3
repos/os/src/test/clipboard/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = test-clipboard
|
||||
SRC_CC = main.cc
|
||||
LIBS = base server
|
Loading…
x
Reference in New Issue
Block a user