app/pike_loader: mouse driven loader client

Component to create and load a Nitpicker view using a mouse drag,
inspired by Rob Pike's Mux, 8½, and Rio window managers.

Fix #71
This commit is contained in:
Emery Hemingway
2017-04-16 12:12:33 -05:00
committed by Norman Feske
parent 7267d88760
commit 78046dfd66
3 changed files with 436 additions and 0 deletions

163
run/pike_loader.run Normal file
View File

@@ -0,0 +1,163 @@
#
# Build
#
if {![have_spec linux]} {
puts "Runs on Linux only"
exit 0
}
set build_components {
core init
app/cli_monitor
app/pike_loader
app/pointer
drivers/framebuffer
drivers/timer
server/dynamic_rom
server/loader
server/nit_fb
server/nitpicker
server/terminal
test/nitpicker
}
build $build_components
create_boot_directory
#
# Generate config
#
append config {
<config>
<parent-provides>
<service name="PD"/>
<service name="CPU"/>
<service name="ROM"/>
<service name="RAM"/>
<service name="RM"/>
<service name="LOG"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<start name="fb_sdl">
<resource name="RAM" quantum="4M"/>
<provides>
<service name="Input"/>
<service name="Framebuffer"/>
</provides>
</start>
<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 verbose="yes">
<domain name="pointer" layer="1" content="client" label="no" origin="pointer" />
<domain name="pike" layer="2" content="client" label="yes"/>
<domain name="default" layer="2" content="client" label="no" focus="click" hover="always" />
<policy label_prefix="pointer" domain="pointer"/>
<policy label_prefix="pike_loader" domain="pike"/>
<default-policy domain="default"/>
<global-key name="KEY_MENU" label="pike_loader -> input" />
<global-key name="KEY_LEFTALT" label="pike_loader -> input" />
</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="loader">
<resource name="RAM" quantum="1M"/>
<provides> <service name="Loader"/> </provides>
<config>
<policy label_prefix="pike_loader">
<parent-rom name="cli_monitor"/>
<parent-rom name="terminal"/>
<parent-rom name="nit_fb"/>
<parent-rom name="init"/>
<parent-rom name="ld.lib.so"/>
</policy>
</config>
</start>
<start name="pike_loader">
<resource name="RAM" quantum="16M"/>
<route>
<any-service> <parent/> <any-child/> </any-service>
</route>
<config>
<start name="dummy">
<binary name="init"/>
<config verbose="true">
<parent-provides>
<service name="CPU"/>
<service name="LOG"/>
<service name="PD"/>
<service name="RAM"/>
<service name="RM"/>
<service name="ROM"/>
<service name="Nitpicker"/>
<service name="Timer"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<start name="nit_fb">
<resource name="RAM" quantum="4M"/>
<provides>
<service name="Framebuffer"/>
<service name="Input"/>
</provides>
</start>
<start name="terminal">
<resource name="RAM" quantum="4M"/>
<provides>
<service name="Terminal"/>
</provides>
</start>
<start name="cli_monitor">
<resource name="RAM" quantum="4M"/>
<config> <vfs/> </config>
</start>
</config>
</start>
</config>
</start>
</config>}
install_config $config
#
# Boot modules
#
# generic modules
set boot_modules {
core ld.lib.so init
cli_monitor
fb_sdl
loader
nit_fb
nitpicker
pike_loader
pointer
terminal
testnit
timer
}
build_boot_image $boot_modules
run_genode_until forever

270
src/app/pike_loader/main.cc Normal file
View File

@@ -0,0 +1,270 @@
/*
* \brief Rio inspired launcher
* \author Emery Hemingway
* \date 2017-04-13
*/
/*
* Copyright (C) 2017 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.
*/
/* Genode includes */
#include <util/geometry.h>
#include <loader_session/connection.h>
#include <nitpicker_session/connection.h>
#include <input/event.h>
#include <base/attached_rom_dataspace.h>
#include <base/component.h>
namespace Morse {
typedef Nitpicker::Session::View_handle View_handle;
typedef Nitpicker::Session::Command Command;
typedef Genode::String<80> String;
struct Main;
};
struct Morse::Main
{
Genode::Env &env;
Genode::Attached_rom_dataspace config_rom { env, "config" };
Nitpicker::Connection nitpicker { env, "input" };
Input::Session_client &input = *nitpicker.input();
Genode::Reconstructible<Loader::Connection> loader {
env, env.ram().avail() - 4096 };
/********************************
** State held between signals **
********************************/
enum Phase { IDLE, ENTER, DRAG, EXIT };
Phase phase = IDLE;
int entry_key = Input::KEY_UNKNOWN;
int entry_count;
int entry_x;
int entry_y;
int exit_x;
int exit_y;
/***************
** Rendering **
***************/
Nitpicker::Session::View_handle view_handle = nitpicker.create_view();
Nitpicker::Point nit_point()
{
return Nitpicker::Point(
entry_x < exit_x ? entry_x : exit_x,
entry_y < exit_y ? entry_y : exit_y);
}
Nitpicker::Area nit_area()
{
return Nitpicker::Area(
entry_x < exit_x ? exit_x - entry_x : entry_x - exit_x,
entry_y < exit_y ? exit_y - entry_y : entry_y - exit_y);
}
Nitpicker::Rect nit_rect()
{
return Nitpicker::Rect(nit_point(), nit_area());
}
void render_drag()
{
nitpicker.enqueue<Command::Geometry>(view_handle, nit_rect());
nitpicker.execute();
}
String loader_binary(Genode::Xml_node const &start_node)
{
using Genode::Xml_node;
String name;
try {
Xml_node binary = start_node.sub_node("binary");
binary.attribute("name").value(&name);
} catch (Xml_node::Nonexistent_sub_node) {
start_node.attribute("name").value(&name);
}
return name;
}
String loader_label(Genode::Xml_node const &start_node)
{
String name;
start_node.attribute("name").value(&name);
return name;
}
void render_loader()
{
using namespace Nitpicker;
using namespace Genode;
/* Reduce the view area to zero unit the loader is ready */
nitpicker.enqueue<Command::Geometry>(
view_handle, Nitpicker::Rect(nit_point(), Nitpicker::Area()));
nitpicker.execute();
/* If the selected view is too tiny, back out */
Nitpicker::Area area = nit_area();
if (area.w() < 16 && area.h() < 16)
return;
Xml_node start_node = config_rom.xml().sub_node("start");
/* We are only patron of single load, so transfer the slack RAM */
loader.construct(env, env.ram().avail() - 4096);
try {
Xml_node config_xml = start_node.sub_node("config");
Attached_dataspace ds(
env.rm(), loader->alloc_rom_module("config", config_xml.size()));
strncpy(ds.local_addr<char>(), config_xml.addr(),
config_xml.size()+1);
loader->commit_rom_module("config");
} catch (Xml_node::Nonexistent_sub_node) { }
loader->view_ready_sigh(view_ready_handler);
loader->parent_view(nitpicker.view_capability(view_handle));
loader->start(loader_binary(start_node).string(),
loader_label(start_node).string());
}
/*********************
** Signal handling **
*********************/
void handle_input()
{
using namespace Input;
input.for_each_event([&] (Event const &ev) {
switch (ev.type()) {
case Event::MOTION:
switch (phase) {
case ENTER:
phase = DRAG;
nitpicker.enqueue<Command::To_front>(view_handle);
entry_x = exit_x = ev.ax();
entry_y = exit_y = ev.ay();
break;
case DRAG:
exit_x = ev.ax();
exit_y = ev.ay();
break;
default: break;
}
break;
case Event::PRESS:
switch (phase) {
case IDLE:
phase = ENTER;
loader.destruct();
entry_key = ev.code();
entry_count = 1;
entry_x = entry_y = exit_x = exit_y = 0;
break;
default: break;
}
break;
case Event::RELEASE:
switch (phase) {
case IDLE:
break;
default:
if (entry_key == ev.code()) {
phase = EXIT;
nitpicker.enqueue<Command::To_front>(view_handle);
}
}
break;
default: break;
}
});
switch (phase) {
case DRAG:
render_drag();
break;
case EXIT:
phase = IDLE;
render_loader();
break;
default:
break;
}
}
Genode::Signal_handler<Main> input_handler {
env.ep(), *this, &Main::handle_input };
void handle_view_ready()
{
Loader::Area const la = loader->view_size();
Nitpicker::Area const na = nit_area();
{
using namespace Loader;
Area area(
Genode::min(la.w(), na.w()),
Genode::min(la.h(), na.h()));
Rect const rect(Point(), area);
{
using namespace Nitpicker;
Point const np = nit_point();
Point const offset_point(
rect.w() < na.w() ? np.x() + (na.w() - rect.w()) / 2 : np.x(),
rect.h() < na.h() ? np.y() + (na.h() - rect.h()) / 2 : np.y());
Rect const offset_rect(offset_point, area);
nitpicker.enqueue<Command::Geometry>(
view_handle, offset_rect);
nitpicker.execute();
}
loader->view_geometry(rect, Point());
}
}
Genode::Signal_handler<Main> view_ready_handler {
env.ep(), *this, &Main::handle_view_ready };
Main(Genode::Env &env) : env(env)
{
input.sigh(input_handler);
}
};
void Component::construct(Genode::Env &env)
{
static Morse::Main main(env);
}

View File

@@ -0,0 +1,3 @@
TARGET = pike_loader
SRC_CC = main.cc
LIBS = base