gems: launcher application

This commit is contained in:
Norman Feske 2014-09-30 16:46:55 +02:00
parent cc303c4671
commit 6244c6ec97
11 changed files with 1918 additions and 0 deletions

177
repos/gems/run/launcher.run Normal file
View File

@ -0,0 +1,177 @@
#
# Build
#
set build_components {
core init drivers/timer drivers/framebuffer drivers/input drivers/pci
server/dynamic_rom server/nitpicker server/report_rom
app/pointer app/menu_view
app/scout app/launchpad app/launcher test/nitpicker
server/nit_fader
}
build $build_components
create_boot_directory
#
# Generate config
#
append config {
<config>
<parent-provides>
<service name="ROM"/>
<service name="CPU"/>
<service name="PD"/>
<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>}
append_if [have_spec sdl] config {
<start name="fb_sdl">
<resource name="RAM" quantum="4M"/>
<provides>
<service name="Input"/>
<service name="Framebuffer"/>
</provides>
</start>}
append_if [have_spec pci] config {
<start name="pci_drv">
<resource name="RAM" quantum="1M"/>
<provides><service name="PCI"/></provides>
</start>}
append_if [have_spec framebuffer] config {
<start name="fb_drv">
<resource name="RAM" quantum="4M"/>
<provides><service name="Framebuffer"/></provides>
<config buffered="yes" />
</start>}
append_if [have_spec ps2] config {
<start name="ps2_drv">
<resource name="RAM" quantum="1M"/>
<provides><service name="Input"/></provides>
</start>}
append config {
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="nitpicker">
<resource name="RAM" quantum="1M"/>
<provides><service name="Nitpicker"/></provides>
<config>
<report xray="yes" />
<domain name="pointer" layer="1" xray="no" origin="pointer" />
<domain name="panel" layer="2" xray="no" />
<domain name="" layer="3" />
<policy label="pointer" domain="pointer"/>
<policy label="launcher -> menu" domain="panel"/>
<policy label="" domain=""/>
<global-key name="KEY_SCROLLLOCK" operation="xray" />
<global-key name="KEY_SYSRQ" operation="kill" />
<global-key name="KEY_PRINT" operation="kill" />
<global-key name="KEY_F11" operation="kill" />
<global-key name="KEY_F12" operation="xray" />
</config>
</start>
<start name="pointer">
<resource name="RAM" quantum="1M"/>
<route>
<service name="Nitpicker"> <child name="nitpicker" /> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
<start name="report_rom">
<resource name="RAM" quantum="1M"/>
<provides> <service name="Report"/> <service name="ROM"/> </provides>
<config>
<rom>
<policy label="launcher -> xray" report="nitpicker -> xray"/>
</rom>
</config>
</start>
<start name="launcher">
<resource name="RAM" quantum="60M" />
<config visibility="xray">
<subsystem name="scout" title="Scout">
<resource name="RAM" quantum="20M" />
<binary name="scout" />
</subsystem>
<subsystem name="testnit1" title="Nitpicker Test">
<resource name="RAM" quantum="2M" />
<binary name="testnit" />
</subsystem>
<subsystem name="testnit2" title="Nitpicker Test 2">
<resource name="RAM" quantum="2M" />
<binary name="testnit" />
</subsystem>
<subsystem name="testnit3" title="Nitpicker Test 3">
<resource name="RAM" quantum="2M" />
<binary name="testnit" />
</subsystem>
<subsystem name="scout2" title="Scoutx">
<resource name="RAM" quantum="20M" />
<binary name="scout" />
</subsystem>
<subsystem name="testnit4" title="Nitpicker Testx">
<resource name="RAM" quantum="2M" />
<binary name="testnit" />
</subsystem>
<subsystem name="testnit5">
<resource name="RAM" quantum="2M" />
<binary name="testnit" />
</subsystem>
<subsystem name="testnit6">
<resource name="RAM" quantum="2M" />
<binary name="testnit" />
</subsystem>
</config>
<route>
<service name="ROM"> <if-arg key="label" value="xray"/>
<child name="report_rom" />
</service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
</config>}
install_config $config
#
# Boot modules
#
# generic modules
set boot_modules {
core init timer dynamic_rom nitpicker pointer menu_view
ld.lib.so libpng.lib.so libc.lib.so libm.lib.so zlib.lib.so
menu_view_styles.tar
scout launchpad testnit
nit_fader report_rom launcher
}
# platform-specific modules
lappend_if [have_spec linux] boot_modules fb_sdl
lappend_if [have_spec pci] boot_modules pci_drv
lappend_if [have_spec ps2] boot_modules ps2_drv
lappend_if [have_spec framebuffer] boot_modules fb_drv
build_boot_image $boot_modules
run_genode_until forever

View File

@ -0,0 +1,230 @@
/*
* \brief Context dialog
* \author Norman Feske
* \date 2014-10-04
*/
/*
* 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.
*/
#ifndef _CONTEXT_DIALOG_H_
#define _CONTEXT_DIALOG_H_
/* local includes */
#include <fading_dialog.h>
namespace Launcher { struct Context_dialog; }
class Launcher::Context_dialog : Input_event_handler, Dialog_generator,
Hover_handler, Dialog_model
{
public:
struct Response_handler
{
virtual void handle_context_kill() = 0;
virtual void handle_context_hide() = 0;
};
private:
struct Element : List<Element>::Element
{
Label label;
bool hovered = false;
bool touched = false;
bool selected = false;
Element(char const *label) : label(label) { }
};
Label _hovered() const
{
for (Element const *e = _elements.first(); e; e = e->next())
if (e->hovered)
return e->label;
return Label("");
}
List<Element> _elements;
void _generate_dialog_elements(Xml_generator &xml)
{
for (Element const *e = _elements.first(); e; e = e->next()) {
xml.node("button", [&] () {
xml.attribute("name", e->label.string());
if ((e->hovered && !_click_in_progress)
|| (e->hovered && e->touched))
xml.attribute("hovered", "yes");
if (e->selected || e->touched)
xml.attribute("selected", "yes");
xml.node("label", [&] () {
xml.attribute("text", e->label.string());
});
});
}
}
void _touch(Label const &label)
{
for (Element *e = _elements.first(); e; e = e->next())
e->touched = (e->label == label);
}
void _reset_hover()
{
for (Element *e = _elements.first(); e; e = e->next())
e->hovered = false;
}
Element _hide_button { "Hide" };
Element _kill_button { "Kill" };
Fading_dialog _dialog;
unsigned _key_cnt = 0;
Label _clicked;
bool _click_in_progress = false;
Label _subsystem;
Response_handler &_response_handler;
public:
Context_dialog(Server::Entrypoint &ep, Cap_session &cap, Ram_session &ram,
Report_rom_slave &report_rom_slave,
Response_handler &response_handler)
:
_dialog(ep, cap, ram, report_rom_slave, "context_dialog", "context_hover",
*this, *this, *this, *this,
Fading_dialog::Position(364, 64)),
_response_handler(response_handler)
{
_elements.insert(&_hide_button);
_elements.insert(&_kill_button);
_dialog.update();
}
/**
* Dialog_generator interface
*/
void generate_dialog(Xml_generator &xml) override
{
xml.node("frame", [&] () {
xml.node("vbox", [&] () {
_generate_dialog_elements(xml);
});
});
}
/**
* Hover_handler interface
*/
void hover_changed(Xml_node hover) override
{
Label const old_hovered = _hovered();
_reset_hover();
try {
Xml_node button = hover.sub_node("dialog")
.sub_node("frame")
.sub_node("vbox")
.sub_node("button");
for (Element *e = _elements.first(); e; e = e->next()) {
Label const label =
Decorator::string_attribute(button, "name", Label(""));
if (e->label == label)
e->hovered = true;
}
} catch (Xml_node::Nonexistent_sub_node) { }
Label const new_hovered = _hovered();
if (old_hovered != new_hovered)
dialog_changed();
}
/**
* Input_event_handler interface
*/
bool handle_input_event(Input::Event const &ev) override
{
if (ev.type() == Input::Event::MOTION)
return true;
if (ev.type() == Input::Event::PRESS) _key_cnt++;
if (ev.type() == Input::Event::RELEASE) _key_cnt--;
if (ev.type() == Input::Event::PRESS
&& ev.keycode() == Input::BTN_LEFT
&& _key_cnt == 1) {
Label const hovered = _hovered();
_click_in_progress = true;
_clicked = hovered;
_touch(hovered);
dialog_changed();
}
if (ev.type() == Input::Event::RELEASE
&& _click_in_progress && _key_cnt == 0) {
Label const hovered = _hovered();
if (_clicked == hovered) {
if (_kill_button.hovered)
_response_handler.handle_context_kill();
if (_hide_button.hovered)
_response_handler.handle_context_hide();
} else {
_touch("");
}
_clicked = Label("");
_click_in_progress = false;
dialog_changed();
}
return false;
}
void visible(bool visible)
{
/* reset touch state when (re-)opening the context dialog */
if (visible) {
_touch("");
_reset_hover();
dialog_changed();
_dialog.update();
}
_dialog.visible(visible);
}
void position(Fading_dialog::Position position)
{
_dialog.position(position);
}
};
#endif /* _CONTEXT_DIALOG_H_ */

View File

@ -0,0 +1,123 @@
/*
* \brief Local nitpicker service provided to dialog slaves
* \author Norman Feske
* \date 2014-10-01
*
* This implementation of the nitpicker interface intercepts the input events
* of a dialog slave to let the launcher respond to events (like mouse clicks)
* directly.
*/
/*
* 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.
*/
#ifndef _DIALOG_NITPICKER_H_
#define _DIALOG_NITPICKER_H_
/* Genode includes */
#include <util/string.h>
#include <os/attached_dataspace.h>
#include <nitpicker_session/nitpicker_session.h>
#include <input_session/client.h>
#include <input/event.h>
#include <input/component.h>
/* gems includes */
#include <gems/wrapped_nitpicker_session.h>
/* local includes */
#include <types.h>
namespace Launcher {
struct Dialog_nitpicker_session;
struct Dialog_nitpicker_service;
}
struct Launcher::Dialog_nitpicker_session : Wrapped_nitpicker_session
{
struct Input_event_handler
{
/**
* Handle input event
*
* \return true if the event should be propagated to the wrapped
* nitpicker session
*/
virtual bool handle_input_event(Input::Event const &ev) = 0;
};
Input_event_handler &_input_event_handler;
Server::Entrypoint &_ep;
Nitpicker::Session &_nitpicker_session;
Input::Session_client _nitpicker_input { _nitpicker_session.input_session() };
Attached_dataspace _nitpicker_input_ds { _nitpicker_input.dataspace() };
Signal_rpc_member<Dialog_nitpicker_session>
_input_dispatcher { _ep, *this, &Dialog_nitpicker_session::_input_handler };
Input::Session_component _input_session;
Capability<Input::Session> _input_session_cap { _ep.manage(_input_session) };
/**
* Constructor
*/
Dialog_nitpicker_session(Nitpicker::Session &nitpicker_session,
Server::Entrypoint &ep,
Input_event_handler &input_event_handler)
:
Wrapped_nitpicker_session(nitpicker_session),
_input_event_handler(input_event_handler),
_ep(ep),
_nitpicker_session(nitpicker_session)
{
_nitpicker_input.sigh(_input_dispatcher);
_input_session.event_queue().enabled(true);
}
~Dialog_nitpicker_session()
{
_ep.dissolve(_input_session);
}
void _input_handler(unsigned)
{
Input::Event const * const events =
_nitpicker_input_ds.local_addr<Input::Event>();
while (_nitpicker_input.is_pending()) {
size_t const num_events = _nitpicker_input.flush();
for (size_t i = 0; i < num_events; i++) {
Input::Event const &ev = events[i];
if (_input_event_handler.handle_input_event(ev))
_input_session.submit(ev);
}
}
}
/*********************************
** Nitpicker session interface **
*********************************/
Input::Session_capability input_session() override
{
return _input_session_cap;
}
};
#endif /* _DIALOG_NITPICKER_H_ */

View File

@ -0,0 +1,254 @@
/*
* \brief Dialog
* \author Norman Feske
* \date 2014-10-02
*/
/*
* 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.
*/
#ifndef _FADING_DIALOG_H_
#define _FADING_DIALOG_H_
/* gems includes */
#include <gems/report_rom_slave.h>
#include <gems/local_reporter.h>
/* local includes */
#include <nit_fader_slave.h>
#include <menu_view_slave.h>
#include <dialog_nitpicker.h>
#include <types.h>
namespace Launcher
{
struct Dialog_generator { virtual void generate_dialog(Xml_generator &) = 0; };
struct Hover_handler { virtual void hover_changed(Xml_node hover) = 0; };
typedef Dialog_nitpicker_session::Input_event_handler Input_event_handler;
class Fading_dialog;
class Dialog_model
{
private:
bool _up_to_date = true;
friend class Fading_dialog;
public:
void dialog_changed() { _up_to_date = false; }
};
}
class Launcher::Fading_dialog : private Input_event_handler
{
private:
Rom_session_capability _dialog_rom;
/* dialog reported locally */
Capability<Report::Session> _dialog_report;
Rom_session_client _hover_rom;
Lazy_volatile_object<Attached_dataspace> _hover_ds;
/* hovered element reported by menu view */
Capability<Report::Session> _hover_report;
Local_reporter _dialog_reporter { "dialog", _dialog_report };
Input_event_handler &_dialog_input_event_handler;
Hover_handler &_hover_handler;
Dialog_generator &_dialog_generator;
Dialog_model &_dialog_model;
void _update_dialog()
{
bool const dialog_needs_update = !_dialog_model._up_to_date;
_dialog_model._up_to_date = false;
if (dialog_needs_update) {
Local_reporter::Xml_generator xml(_dialog_reporter, [&] ()
{
_dialog_generator.generate_dialog(xml);
});
}
}
/**
* Input_event_handler interface
*/
bool handle_input_event(Input::Event const &ev) override
{
bool const forward_event = _dialog_input_event_handler.handle_input_event(ev);
_update_dialog();
return forward_event;
}
void _handle_hover_update(unsigned)
{
try {
if (!_hover_ds.is_constructed() || _hover_rom.update() == false) {
if (_hover_ds.is_constructed())
_hover_ds->invalidate();
_hover_ds.construct(_hover_rom.dataspace());
}
Xml_node hover(_hover_ds->local_addr<char>());
_hover_handler.hover_changed(hover);
bool const dialog_needs_update = !_dialog_model._up_to_date;
_dialog_model._up_to_date = true;
if (dialog_needs_update) {
Local_reporter::Xml_generator xml(_dialog_reporter, [&] ()
{
_dialog_generator.generate_dialog(xml);
});
}
} catch (...) {
PWRN("no menu hover model available");
}
}
Signal_rpc_member<Fading_dialog> _hover_update_dispatcher;
private:
/**
* Local nitpicker service to be handed out to the menu view slave
*/
struct Nitpicker_service : Genode::Service
{
Server::Entrypoint &ep;
Rpc_entrypoint &child_ep;
/* connection to real nitpicker */
Nitpicker::Connection connection { "menu" };
Dialog_nitpicker_session wrapper_session;
Capability<Nitpicker::Session> session_cap { child_ep.manage(&wrapper_session) };
Nitpicker_service(Server::Entrypoint &ep,
Rpc_entrypoint &child_ep,
Dialog_nitpicker_session::Input_event_handler &ev_handler)
:
Genode::Service(Nitpicker::Session::service_name()),
ep(ep), child_ep(child_ep),
wrapper_session(connection, ep, ev_handler)
{ }
/*******************************
** Genode::Service interface **
*******************************/
Genode::Session_capability
session(const char *, Genode::Affinity const &) override
{
return session_cap;
}
void upgrade(Genode::Session_capability, const char *args) override
{
PDBG("upgrade called args: '%s'", args);
}
void close(Genode::Session_capability) override { }
};
/*
* Entrypoint for the fader slave
*
* This entrypoint is used for handling the parent interface for the
* fader slave and for providing the wrapped nitpicker service to the
* slave. The latter cannot be provided by the main entrypoint because
* during the construction of the 'nit_fader_slave' (executed in the
* context of the main entrypoint), the slave tries to create a
* nitpicker session (to be answered with the wrapped session).
*/
size_t const _fader_slave_ep_stack_size = 4*1024*sizeof(addr_t);
Rpc_entrypoint _fader_slave_ep;
Nitpicker_service _nitpicker_service;
Nit_fader_slave _nit_fader_slave;
Menu_view_slave _menu_view_slave;
public:
typedef Menu_view_slave::Position Position;
/**
* Constructor
*
* \param ep main entrypoint, used for managing the local input
* session provided (indirectly through the wrapped
* nitpicker session) to the menu view
* \param cap capability session to be used for creating the
* slave entrypoints
* \param ram RAM session where to draw the memory for providing
* configuration data to the slave processes
*/
Fading_dialog(Server::Entrypoint &ep,
Cap_session &cap,
Ram_session &ram,
Report_rom_slave &report_rom_slave,
char const *dialog_name,
char const *hover_name,
Input_event_handler &input_event_handler,
Hover_handler &hover_handler,
Dialog_generator &dialog_generator,
Dialog_model &dialog_model,
Position initial_position)
:
_dialog_rom(report_rom_slave.rom_session(dialog_name)),
_dialog_report(report_rom_slave.report_session(dialog_name)),
_hover_rom(report_rom_slave.rom_session(hover_name)),
_hover_report(report_rom_slave.report_session(hover_name)),
_dialog_input_event_handler(input_event_handler),
_hover_handler(hover_handler),
_dialog_generator(dialog_generator),
_dialog_model(dialog_model),
_hover_update_dispatcher(ep, *this, &Fading_dialog::_handle_hover_update),
_fader_slave_ep(&cap, _fader_slave_ep_stack_size, "nit_fader"),
_nitpicker_service(ep, _fader_slave_ep, *this),
_nit_fader_slave(_fader_slave_ep, ram, _nitpicker_service),
_menu_view_slave(cap, ram, _nit_fader_slave.nitpicker_session("menu"),
_dialog_rom, _hover_report, initial_position)
{
Rom_session_client(_hover_rom).sigh(_hover_update_dispatcher);
}
void update()
{
Local_reporter::Xml_generator xml(_dialog_reporter, [&] ()
{
_dialog_generator.generate_dialog(xml);
});
}
void visible(bool visible) { _nit_fader_slave.visible(visible); }
void position(Position position) { _menu_view_slave.position(position); }
};
#endif /* _FADING_DIALOG_H_ */

View File

@ -0,0 +1,147 @@
/*
* \brief Launcher
* \author Norman Feske
* \date 2014-09-30
*/
/*
* 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 <os/server.h>
#include <os/config.h>
#include <cap_session/connection.h>
#include <decorator/xml_utils.h>
#include <util/volatile_object.h>
#include <os/attached_rom_dataspace.h>
#include <nitpicker_session/connection.h>
/* local includes */
#include <menu_dialog.h>
namespace Launcher { struct Main; }
struct Launcher::Main
{
Server::Entrypoint ep;
Genode::Cap_connection cap;
char const *report_rom_config =
"<config> <rom>"
" <policy label=\"menu_dialog\" report=\"menu_dialog\"/>"
" <policy label=\"menu_hover\" report=\"menu_hover\"/>"
" <policy label=\"context_dialog\" report=\"context_dialog\"/>"
" <policy label=\"context_hover\" report=\"context_hover\"/>"
"</rom> </config>";
Report_rom_slave report_rom_slave = { cap, *env()->ram_session(), report_rom_config };
/**
* Nitpicker session used to perform session-control operations on the
* subsystem's nitpicker sessions.
*/
Nitpicker::Connection nitpicker;
Subsystem_manager subsystem_manager { ep, cap };
Menu_dialog menu_dialog { ep, cap, *env()->ram_session(), report_rom_slave,
subsystem_manager, nitpicker };
Lazy_volatile_object<Attached_rom_dataspace> xray_rom_ds;
enum Visibility { VISIBILITY_ALWAYS, VISIBILITY_XRAY };
Visibility visibility = VISIBILITY_ALWAYS;
void handle_config(unsigned);
Genode::Signal_rpc_member<Main> xray_update_dispatcher =
{ ep, *this, &Main::handle_xray_update };
void handle_xray_update(unsigned);
/**
* Constructor
*/
Main(Server::Entrypoint &ep) : ep(ep)
{
handle_config(0);
if (visibility == VISIBILITY_ALWAYS)
menu_dialog.visible(true);
}
};
void Launcher::Main::handle_config(unsigned)
{
config()->reload();
/* set default visibility */
visibility = VISIBILITY_ALWAYS;
/* obtain model about nitpicker's xray mode */
if (config()->xml_node().has_attribute("visibility")) {
if (config()->xml_node().attribute("visibility").has_value("xray")) {
xray_rom_ds.construct("xray");
xray_rom_ds->sigh(xray_update_dispatcher);
visibility = VISIBILITY_XRAY;
/* manually import the initial xray state */
handle_xray_update(0);
}
}
menu_dialog.update();
}
void Launcher::Main::handle_xray_update(unsigned)
{
xray_rom_ds->update();
if (!xray_rom_ds->is_valid()) {
PWRN("could not access xray info");
menu_dialog.visible(false);
return;
}
Xml_node xray(xray_rom_ds->local_addr<char>());
bool const visible = xray.has_attribute("enabled")
&& xray.attribute("enabled").has_value("yes");
menu_dialog.visible(visible);
}
/************
** Server **
************/
namespace Server {
char const *name() { return "desktop_ep"; }
size_t stack_size() { return 4*1024*sizeof(long); }
void construct(Entrypoint &ep)
{
/* look for dynamic linker */
try {
static Rom_connection rom("ld.lib.so");
Process::dynamic_linker(rom.dataspace());
} catch (...) { }
static Launcher::Main desktop(ep);
}
}

View File

@ -0,0 +1,387 @@
/*
* \brief Menu dialog
* \author Norman Feske
* \date 2014-10-04
*/
/*
* 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.
*/
#ifndef _MENU_DIALOG_H_
#define _MENU_DIALOG_H_
/* Genode includes */
#include <timer_session/connection.h>
/* local includes */
#include <fading_dialog.h>
#include <subsystem_manager.h>
#include <context_dialog.h>
namespace Launcher { struct Menu_dialog; }
class Launcher::Menu_dialog : Input_event_handler, Dialog_generator,
Hover_handler, Dialog_model,
Context_dialog::Response_handler
{
private:
typedef String<128> Title;
struct Element : List<Element>::Element
{
Label label;
Title title;
bool hovered = false;
bool touched = false;
bool running = false;
Element(Xml_node node)
:
label(Decorator::string_attribute(node, "name", Label(""))),
title(Decorator::string_attribute(node, "title", Title(label.string())))
{ }
};
List<Element> _elements;
void _generate_dialog_elements(Xml_generator &xml)
{
for (Element const *e = _elements.first(); e; e = e->next()) {
xml.node("button", [&] () {
xml.attribute("name", e->label.string());
if ((e->hovered && !_click_in_progress)
|| (e->hovered && e->touched))
xml.attribute("hovered", "yes");
if (e->running || e->touched)
xml.attribute("selected", "yes");
xml.node("label", [&] () {
xml.attribute("text", e->title.string());
});
});
}
}
class Lookup_failed { };
Element const &_lookup_const(Label const &label) const
{
for (Element const *e = _elements.first(); e; e = e->next())
if (e->label == label)
return *e;
throw Lookup_failed();
}
Element &_lookup(Label const &label)
{
for (Element *e = _elements.first(); e; e = e->next())
if (e->label == label)
return *e;
throw Lookup_failed();
}
Fading_dialog::Position _position { 32, 32 };
Timer::Connection _timer;
Subsystem_manager &_subsystem_manager;
Nitpicker::Session &_nitpicker;
Fading_dialog _dialog;
Rect _hovered_rect;
unsigned _key_cnt = 0;
Label _clicked;
bool _click_in_progress = false;
Signal_rpc_member<Menu_dialog> _timer_dispatcher;
Label _context_subsystem;
Context_dialog _context_dialog;
Label _hovered() const
{
for (Element const *e = _elements.first(); e; e = e->next())
if (e->hovered)
return e->label;
return Label("");
}
bool _running(Label const &label) const
{
try { return _lookup_const(label).running; }
catch (Lookup_failed) { return false; }
}
void _running(Label const &label, bool running)
{
try { _lookup(label).running = running; }
catch (Lookup_failed) { }
}
void _touch(Label const &label)
{
for (Element *e = _elements.first(); e; e = e->next())
e->touched = (e->label == label);
}
/**
* Lookup subsystem in config
*/
static Xml_node _subsystem(Xml_node config, char const *name)
{
Xml_node node = config.sub_node("subsystem");
for (;; node = node.next("subsystem")) {
if (node.attribute("name").has_value(name))
return node;
}
}
void _start(Label const &label)
{
try {
_subsystem_manager.start(_subsystem(config()->xml_node(),
label.string()));
_running(label, true);
dialog_changed();
} catch (Xml_node::Nonexistent_sub_node) {
PERR("no subsystem config found for \"%s\"", label.string());
} catch (Subsystem_manager::Invalid_config) {
PERR("invalid subsystem configuration for \"%s\"", label.string());
}
}
void _kill(Label const &label)
{
_subsystem_manager.kill(label.string());
_running(label, false);
dialog_changed();
_dialog.update();
_context_dialog.visible(false);
}
void _hide(Label const &label)
{
_nitpicker.session_control(selector(label.string()),
Nitpicker::Session::SESSION_CONTROL_HIDE);
_context_dialog.visible(false);
}
void _handle_timer(unsigned)
{
if (_click_in_progress && _hovered() == _clicked) {
_touch("");
Fading_dialog::Position position(_hovered_rect.p2().x(),
_hovered_rect.p1().y() - 44);
_context_subsystem = _clicked;
_context_dialog.position(_position + position);
_context_dialog.visible(true);
}
_click_in_progress = false;
}
public:
Menu_dialog(Server::Entrypoint &ep, Cap_session &cap, Ram_session &ram,
Report_rom_slave &report_rom_slave,
Subsystem_manager &subsystem_manager,
Nitpicker::Session &nitpicker)
:
_subsystem_manager(subsystem_manager),
_nitpicker(nitpicker),
_dialog(ep, cap, ram, report_rom_slave, "menu_dialog", "menu_hover",
*this, *this, *this, *this,
_position),
_timer_dispatcher(ep, *this, &Menu_dialog::_handle_timer),
_context_dialog(ep, cap, ram, report_rom_slave, *this)
{
_timer.sigh(_timer_dispatcher);
}
/**
* Dialog_generator interface
*/
void generate_dialog(Xml_generator &xml) override
{
xml.node("frame", [&] () {
xml.node("vbox", [&] () {
_generate_dialog_elements(xml);
});
});
}
Rect _hovered_button_rect(Xml_node hover) const
{
Point p(0, 0);
for (;; hover = hover.sub_node()) {
p = p + Point(point_attribute(hover));
if (hover.has_type("button"))
return Rect(p, area_attribute(hover));
if (!hover.num_sub_nodes())
break;
}
return Rect();
}
/**
* Hover_handler interface
*/
void hover_changed(Xml_node hover) override
{
Label const old_hovered = _hovered();
for (Element *e = _elements.first(); e; e = e->next())
e->hovered = false;
try {
Xml_node button = hover.sub_node("dialog")
.sub_node("frame")
.sub_node("vbox")
.sub_node("button");
for (Element *e = _elements.first(); e; e = e->next()) {
Label const label =
Decorator::string_attribute(button, "name", Label(""));
if (e->label == label) {
e->hovered = true;
_hovered_rect = _hovered_button_rect(hover);
}
}
} catch (Xml_node::Nonexistent_sub_node) { }
Label const new_hovered = _hovered();
if (old_hovered != new_hovered)
dialog_changed();
}
/**
* Input_event_handler interface
*/
bool handle_input_event(Input::Event const &ev) override
{
if (ev.type() == Input::Event::MOTION)
return true;
if (ev.type() == Input::Event::PRESS) _key_cnt++;
if (ev.type() == Input::Event::RELEASE) _key_cnt--;
if (ev.type() == Input::Event::PRESS
&& ev.keycode() == Input::BTN_LEFT
&& _key_cnt == 1) {
_context_dialog.visible(false);
Label const hovered = _hovered();
_click_in_progress = true;
_clicked = hovered;
_touch(hovered);
enum { CONTEXT_DELAY = 500 };
if (_running(hovered)) {
_nitpicker.session_control(selector(hovered.string()),
Nitpicker::Session::SESSION_CONTROL_TO_FRONT);
_nitpicker.session_control(selector(hovered.string()),
Nitpicker::Session::SESSION_CONTROL_SHOW);
_timer.trigger_once(CONTEXT_DELAY*1000);
}
}
if (ev.type() == Input::Event::RELEASE
&& _click_in_progress && _key_cnt == 0) {
Label const hovered = _hovered();
if (_clicked == hovered) {
if (!_running(hovered))
_start(hovered);
} else {
_touch("");
}
_clicked = Label("");
_click_in_progress = false;
}
return false;
}
/**
* Context_dialog::Response_handler interface
*/
void handle_context_kill() override
{
_kill(_context_subsystem);
}
/**
* Context_dialog::Response_handler interface
*/
void handle_context_hide() override
{
_hide(_context_subsystem);
}
void visible(bool visible)
{
_dialog.visible(visible);
if (!visible)
_context_dialog.visible(false);
}
void update()
{
if (_elements.first()) {
PERR("subsequent updates are not supported");
return;
}
Element *last = nullptr;
Xml_node subsystems = config()->xml_node();
subsystems.for_each_sub_node("subsystem",
[&] (Xml_node subsystem)
{
Element * const e = new (env()->heap()) Element(subsystem);
_elements.insert(e, last);
last = e;
});
_dialog.update();
}
};
#endif /* _MENU_DIALOG_H_ */

View File

@ -0,0 +1,161 @@
/*
* \brief Slave used for presenting the menu
* \author Norman Feske
* \date 2014-09-30
*/
/*
* 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.
*/
#ifndef _MENU_VIEW_SLAVE_H_
#define _MENU_VIEW_SLAVE_H_
/* Genode includes */
#include <os/slave.h>
#include <nitpicker_session/nitpicker_session.h>
/* gems includes */
#include <gems/single_session_service.h>
/* local includes */
#include <types.h>
namespace Launcher { class Menu_view_slave; }
class Launcher::Menu_view_slave
{
public:
typedef Surface_base::Point Position;
private:
class Policy : public Genode::Slave_policy
{
private:
Lock mutable _nitpicker_root_lock { Lock::LOCKED };
Capability<Root> _nitpicker_root_cap;
Single_session_service _nitpicker_service;
Single_session_service _dialog_rom_service;
Single_session_service _hover_report_service;
Position _position;
protected:
char const **_permitted_services() const
{
static char const *permitted_services[] = {
"ROM", "CAP", "LOG", "SIGNAL", "RM", "Timer", 0 };
return permitted_services;
};
private:
void _configure(Position pos)
{
char config[1024];
snprintf(config, sizeof(config),
"<config xpos=\"%d\" ypos=\"%d\">\n"
" <report hover=\"yes\"/>\n"
" <libc>\n"
" <vfs>\n"
" <tar name=\"menu_view_styles.tar\" />\n"
" </vfs>\n"
" </libc>\n"
"</config>",
pos.x(), pos.y());
configure(config);
}
public:
Policy(Genode::Rpc_entrypoint &entrypoint,
Genode::Ram_session &ram,
Capability<Nitpicker::Session> nitpicker_session,
Capability<Rom_session> dialog_rom_session,
Capability<Report::Session> hover_report_session,
Position position)
:
Slave_policy("menu_view", entrypoint, &ram),
_nitpicker_service(Nitpicker::Session::service_name(), nitpicker_session),
_dialog_rom_service(Rom_session::service_name(), dialog_rom_session),
_hover_report_service(Report::Session::service_name(), hover_report_session),
_position(position)
{
_configure(position);
}
void position(Position pos)
{
_configure(pos);
}
Genode::Service *resolve_session_request(const char *service_name,
const char *args) override
{
using Genode::strcmp;
if (strcmp(service_name, "Nitpicker") == 0)
return &_nitpicker_service;
char label[128];
Arg_string::find_arg(args, "label").string(label, sizeof(label), "");
if (strcmp(service_name, "ROM") == 0) {
if (strcmp(label, "menu_view -> dialog") == 0)
return &_dialog_rom_service;
}
if (strcmp(service_name, "Report") == 0) {
if (strcmp(label, "menu_view -> hover") == 0)
return &_hover_report_service;
}
return Genode::Slave_policy::resolve_session_request(service_name, args);
}
};
Genode::size_t const _ep_stack_size = 4*1024*sizeof(Genode::addr_t);
Genode::Rpc_entrypoint _ep;
Policy _policy;
Genode::size_t const _quota = 4*1024*1024;
Genode::Slave _slave;
public:
/**
* Constructor
*
* \param ep entrypoint used for child thread
* \param ram RAM session used to allocate the configuration
* dataspace
*/
Menu_view_slave(Genode::Cap_session &cap, Genode::Ram_session &ram,
Capability<Nitpicker::Session> nitpicker_session,
Capability<Rom_session> dialog_rom_session,
Capability<Report::Session> hover_report_session,
Position initial_position)
:
_ep(&cap, _ep_stack_size, "nit_fader"),
_policy(_ep, ram, nitpicker_session, dialog_rom_session,
hover_report_session, initial_position),
_slave(_ep, _policy, _quota)
{ }
void position(Position position) { _policy.position(position); }
};
#endif /* _NIT_FADER_SLAVE_H_ */

View File

@ -0,0 +1,153 @@
/*
* \brief Slave used for toggling the visibility of a nitpicker session
* \author Norman Feske
* \date 2014-09-30
*/
/*
* 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.
*/
#ifndef _NIT_FADER_SLAVE_H_
#define _NIT_FADER_SLAVE_H_
/* Genode includes */
#include <os/slave.h>
#include <nitpicker_session/nitpicker_session.h>
/* local includes */
#include <types.h>
namespace Launcher { class Nit_fader_slave; }
class Launcher::Nit_fader_slave
{
private:
class Policy : public Slave_policy
{
private:
Genode::Service &_nitpicker_service;
Lock mutable _nitpicker_root_lock { Lock::LOCKED };
Capability<Root> _nitpicker_root_cap;
protected:
char const **_permitted_services() const
{
static char const *permitted_services[] = {
"CAP", "LOG", "SIGNAL", "RM", "Timer", 0 };
return permitted_services;
};
public:
Policy(Rpc_entrypoint &entrypoint,
Ram_session &ram,
Genode::Service &nitpicker_service)
:
Slave_policy("nit_fader", entrypoint, &ram),
_nitpicker_service(nitpicker_service)
{
visible(false);
}
void visible(bool visible)
{
char config[256];
snprintf(config, sizeof(config),
"<config alpha=\"%d\" />", visible ? 255 : 0);
configure(config, strlen(config) + 1);
}
bool announce_service(const char *service_name,
Root_capability root,
Allocator *,
Genode::Server *)
{
if (strcmp(service_name, "Nitpicker") == 0)
_nitpicker_root_cap = root;
else
return false;
if (_nitpicker_root_cap.valid())
_nitpicker_root_lock.unlock();
return true;
}
Genode::Service *resolve_session_request(const char *service_name,
const char *args) override
{
if (Genode::strcmp(service_name, "Nitpicker") == 0)
return &_nitpicker_service;
return Genode::Slave_policy::resolve_session_request(service_name, args);
}
Root_capability nitpicker_root() const
{
Lock::Guard guard(_nitpicker_root_lock);
return _nitpicker_root_cap;
}
};
Policy _policy;
size_t const _quota = 2*1024*1024;
Slave _slave;
Root_client _nitpicker_root;
public:
/**
* Constructor
*
* \param ep entrypoint used for nitpicker child thread
* \param ram RAM session used to allocate the configuration
* dataspace
*/
Nit_fader_slave(Rpc_entrypoint &ep, Ram_session &ram,
Genode::Service &nitpicker_service)
:
_policy(ep, ram, nitpicker_service),
_slave(ep, _policy, _quota),
_nitpicker_root(_policy.nitpicker_root())
{
visible(false);
}
Capability<Nitpicker::Session> nitpicker_session(char const *label)
{
enum { ARGBUF_SIZE = 128 };
char argbuf[ARGBUF_SIZE];
argbuf[0] = 0;
/*
* Declare ram-quota donation
*/
enum { SESSION_METADATA = 8*1024 };
Arg_string::set_arg(argbuf, sizeof(argbuf), "ram_quota", SESSION_METADATA);
/*
* Set session label
*/
Arg_string::set_arg(argbuf, sizeof(argbuf), "label", label);
Session_capability session_cap = _nitpicker_root.session(argbuf, Affinity());
return static_cap_cast<Nitpicker::Session>(session_cap);
}
void visible(bool visible)
{
_policy.visible(visible);
}
};
#endif /* _NIT_FADER_SLAVE_H_ */

View File

@ -0,0 +1,237 @@
/*
* \brief Management of subsystems
* \author Norman Feske
* \date 2014-10-02
*/
/*
* 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.
*/
#ifndef _SUBSYSTEM_MANAGER_H_
#define _SUBSYSTEM_MANAGER_H_
/* Genode includes */
#include <os/server.h>
#include <decorator/xml_utils.h>
/* CLI-monitor includes */
#include <cli_monitor/child.h>
namespace Launcher {
class Subsystem_manager;
using Decorator::string_attribute;
}
/***************
** Utilities **
***************/
/*
* XXX copied from 'cli_monitor/main.cc'
*/
static Genode::size_t ram_preservation_from_config()
{
Genode::Number_of_bytes ram_preservation = 0;
try {
Genode::Xml_node node =
Genode::config()->xml_node().sub_node("preservation");
if (node.attribute("name").has_value("RAM"))
node.attribute("quantum").value(&ram_preservation);
} catch (...) { }
return ram_preservation;
}
class Launcher::Subsystem_manager
{
public:
/**
* Exception types
*/
class Invalid_config { };
private:
Server::Entrypoint &_ep;
Cap_session &_cap;
struct Child : Child_base, List<Child>::Element
{
typedef String<128> Binary_name;
Child(Ram &ram,
Label const &label,
Binary_name const &binary,
Cap_session &cap_session,
size_t ram_quota,
size_t ram_limit,
Signal_context_capability yield_response_sig_cap)
:
Child_base(ram,
label.string(),
binary.string(),
cap_session,
ram_quota,
ram_limit,
yield_response_sig_cap)
{ }
};
List<Child> _children;
void _try_response_to_resource_request()
{
for (Child *child = _children.first(); child; child = child->next())
child->try_response_to_resource_request();
}
Genode::Signal_rpc_member<Subsystem_manager> _yield_broadcast_dispatcher =
{ _ep, *this, &Subsystem_manager::_handle_yield_broadcast };
void _handle_yield_broadcast(unsigned)
{
_try_response_to_resource_request();
/*
* XXX copied from 'cli_monitor/main.cc'
*/
/*
* Compute argument of yield request to be broadcasted to all
* processes.
*/
size_t amount = 0;
/* amount needed to reach preservation limit */
Ram::Status ram_status = _ram.status();
if (ram_status.avail < ram_status.preserve)
amount += ram_status.preserve - ram_status.avail;
/* sum of pending resource requests */
for (Child *child = _children.first(); child; child = child->next())
amount += child->requested_ram_quota();
for (Child *child = _children.first(); child; child = child->next())
child->yield(amount, true);
}
Genode::Signal_rpc_member<Subsystem_manager> _resource_avail_dispatcher =
{ _ep, *this, &Subsystem_manager::_handle_resource_avail };
void _handle_resource_avail(unsigned)
{
_try_response_to_resource_request();
}
Genode::Signal_rpc_member<Subsystem_manager> _yield_response_dispatcher =
{ _ep, *this, &Subsystem_manager::_handle_yield_response };
void _handle_yield_response(unsigned)
{
_try_response_to_resource_request();
}
Ram _ram { ram_preservation_from_config(),
_yield_broadcast_dispatcher,
_resource_avail_dispatcher };
static Child::Binary_name _binary_name(Xml_node subsystem)
{
try {
return string_attribute(subsystem.sub_node("binary"),
"name", Child::Binary_name(""));
} catch (Xml_node::Nonexistent_sub_node) {
PERR("missing <binary> definition");
throw Invalid_config();
}
}
struct Ram_config { Number_of_bytes quantum, limit; };
static Ram_config _ram_config(Xml_node subsystem)
{
Number_of_bytes quantum = 0, limit = 0;
try {
subsystem.for_each_sub_node("resource", [&] (Xml_node rsc) {
if (rsc.attribute("name").has_value("RAM")) {
rsc.attribute("quantum").value(&quantum);
if (rsc.has_attribute("limit"))
rsc.attribute("limit").value(&limit);
}
});
} catch (...) {
PERR("invalid RAM resource declaration");
throw Invalid_config();
}
return Ram_config { quantum, limit };
}
public:
Subsystem_manager(Server::Entrypoint &ep, Cap_session &cap)
:
_ep(ep), _cap(cap)
{ }
/**
* Start subsystem
*
* \throw Invalid_config
*/
void start(Xml_node subsystem)
{
Child::Binary_name const binary_name = _binary_name(subsystem);
Child::Label const label = string_attribute(subsystem, "name",
Child::Label(""));
Ram_config const ram_config = _ram_config(subsystem);
PINF("starting child '%s'", label.string());
try {
Child *child = new (env()->heap())
Child(_ram, label, binary_name.string(), _cap,
ram_config.quantum, ram_config.limit,
_yield_broadcast_dispatcher);
/* configure child */
try {
Xml_node config_node = subsystem.sub_node("config");
child->configure(config_node.addr(), config_node.size());
} catch (...) { }
_children.insert(child);
child->start();
} catch (Rom_connection::Rom_connection_failed) {
PERR("binary \"%s\" is missing", binary_name.string());
throw Invalid_config();
}
}
void kill(char const *label)
{
for (Child *c = _children.first(); c; c = c->next()) {
if (c->label() == Child::Label(label)) {
_children.remove(c);
destroy(env()->heap(), c);
return;
}
}
}
};
#endif /* _SUBSYSTEM_MANAGER_H_ */

View File

@ -0,0 +1,4 @@
TARGET = launcher
SRC_CC = main.cc
LIBS = base server config
INC_DIR += $(PRG_DIR)

View File

@ -0,0 +1,45 @@
/*
* \brief Common types for launcher
* \author Norman Feske
* \date 2014-09-30
*/
/*
* 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.
*/
#ifndef _TYPES_H_
#define _TYPES_H_
/* Genode includes */
#include <decorator/xml_utils.h>
#include <nitpicker_session/nitpicker_session.h>
namespace Launcher {
using namespace Genode;
typedef String<128> Label;
static Nitpicker::Session::Label selector(Label label)
{
/*
* Append label separator to uniquely identify the subsystem.
* Otherwise, the selector may be ambiguous if the label of one
* subsystem starts with the label of another subsystem.
*/
char selector[Nitpicker::Session::Label::size()];
snprintf(selector, sizeof(selector), "%s ->", label.string());
return Nitpicker::Session::Label(selector);
}
using Decorator::area_attribute;
using Decorator::point_attribute;
typedef Nitpicker::Point Point;
typedef Nitpicker::Rect Rect;
}
#endif /* _TYPES_H_ */