474 lines
11 KiB
C++
474 lines
11 KiB
C++
/*
|
|
* \brief Local nitpicker service provided to decorator
|
|
* \author Norman Feske
|
|
* \date 2014-02-14
|
|
*/
|
|
|
|
/*
|
|
* 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 _DECORATOR_NITPICKER_H_
|
|
#define _DECORATOR_NITPICKER_H_
|
|
|
|
/* Genode includes */
|
|
#include <util/string.h>
|
|
#include <os/server.h>
|
|
#include <os/attached_dataspace.h>
|
|
#include <nitpicker_session/connection.h>
|
|
#include <input_session/client.h>
|
|
#include <input/event.h>
|
|
#include <input/component.h>
|
|
|
|
/* local includes */
|
|
#include <window_registry.h>
|
|
|
|
namespace Wm { class Main;
|
|
using Genode::size_t;
|
|
using Genode::Allocator;
|
|
using Server::Entrypoint;
|
|
using Genode::Ram_session_client;
|
|
using Genode::Ram_session_capability;
|
|
using Genode::Arg_string;
|
|
using Genode::Object_pool;
|
|
using Genode::Attached_dataspace;
|
|
using Genode::Attached_ram_dataspace;
|
|
using Genode::Signal_rpc_member;
|
|
}
|
|
|
|
|
|
namespace Wm {
|
|
|
|
struct Decorator_nitpicker_session;
|
|
struct Decorator_nitpicker_service;
|
|
struct Decorator_content_callback;
|
|
struct Decorator_content_registry;
|
|
}
|
|
|
|
|
|
struct Wm::Decorator_content_callback
|
|
{
|
|
virtual void content_geometry(Window_registry::Id win_id, Rect rect) = 0;
|
|
|
|
virtual Nitpicker::View_capability content_view(Window_registry::Id win_id) = 0;
|
|
|
|
virtual void update_content_child_views(Window_registry::Id win_id) = 0;
|
|
};
|
|
|
|
|
|
class Wm::Decorator_content_registry
|
|
{
|
|
public:
|
|
|
|
/**
|
|
* Exception type
|
|
*/
|
|
struct Lookup_failed { };
|
|
|
|
private:
|
|
|
|
struct Entry : List<Entry>::Element
|
|
{
|
|
Nitpicker::Session::View_handle const decorator_view_handle;
|
|
Window_registry::Id const win_id;
|
|
|
|
Entry(Nitpicker::Session::View_handle decorator_view_handle,
|
|
Window_registry::Id win_id)
|
|
:
|
|
decorator_view_handle(decorator_view_handle),
|
|
win_id(win_id)
|
|
{ }
|
|
};
|
|
|
|
List<Entry> _list;
|
|
Allocator &_entry_alloc;
|
|
|
|
Entry const &_lookup(Nitpicker::Session::View_handle view_handle) const
|
|
{
|
|
for (Entry const *e = _list.first(); e; e = e->next()) {
|
|
if (e->decorator_view_handle == view_handle)
|
|
return *e;
|
|
}
|
|
|
|
throw Lookup_failed();
|
|
}
|
|
|
|
void _remove(Entry const &e)
|
|
{
|
|
_list.remove(&e);
|
|
destroy(_entry_alloc, const_cast<Entry *>(&e));
|
|
}
|
|
|
|
public:
|
|
|
|
Decorator_content_registry(Allocator &entry_alloc)
|
|
:
|
|
_entry_alloc(entry_alloc)
|
|
{ }
|
|
|
|
~Decorator_content_registry()
|
|
{
|
|
while (Entry *e = _list.first())
|
|
_remove(*e);
|
|
}
|
|
|
|
void insert(Nitpicker::Session::View_handle decorator_view_handle,
|
|
Window_registry::Id win_id)
|
|
{
|
|
Entry *e = new (_entry_alloc) Entry(decorator_view_handle, win_id);
|
|
_list.insert(e);
|
|
}
|
|
|
|
/**
|
|
* Lookup window ID for a given decorator content view
|
|
*
|
|
* \throw Lookup_failed
|
|
*/
|
|
Window_registry::Id lookup(Nitpicker::Session::View_handle view_handle) const
|
|
{
|
|
return _lookup(view_handle).win_id;
|
|
}
|
|
|
|
/**
|
|
* Remove entry
|
|
*
|
|
* \throw Lookup_failed
|
|
*/
|
|
void remove(Nitpicker::Session::View_handle view_handle)
|
|
{
|
|
_remove(_lookup(view_handle));
|
|
}
|
|
};
|
|
|
|
|
|
struct Wm::Decorator_nitpicker_session : Genode::Rpc_object<Nitpicker::Session>
|
|
{
|
|
typedef Nitpicker::View_capability View_capability;
|
|
typedef Nitpicker::Session::View_handle View_handle;
|
|
|
|
Ram_session_client _ram;
|
|
|
|
Nitpicker::Connection _nitpicker_session { "decorator" };
|
|
|
|
typedef Nitpicker::Session::Command_buffer Command_buffer;
|
|
|
|
Attached_ram_dataspace _command_ds { &_ram, sizeof(Command_buffer) };
|
|
|
|
Command_buffer &_command_buffer = *_command_ds.local_addr<Command_buffer>();
|
|
|
|
Input::Session_client _nitpicker_input { _nitpicker_session.input_session() };
|
|
|
|
Attached_dataspace _nitpicker_input_ds { _nitpicker_input.dataspace() };
|
|
|
|
Local_reporter &_pointer_reporter;
|
|
|
|
Input::Session_component &_window_layouter_input;
|
|
|
|
Decorator_content_callback &_content_callback;
|
|
|
|
/* XXX don't allocate content-registry entries from heap */
|
|
Decorator_content_registry _content_registry { *Genode::env()->heap() };
|
|
|
|
Entrypoint &_ep;
|
|
|
|
Allocator &_md_alloc;
|
|
|
|
Signal_rpc_member<Decorator_nitpicker_session>
|
|
_input_dispatcher { _ep, *this, &Decorator_nitpicker_session::_input_handler };
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* \param ep entrypoint used for dispatching signals
|
|
*/
|
|
Decorator_nitpicker_session(Ram_session_capability ram,
|
|
Entrypoint &ep, Allocator &md_alloc,
|
|
Local_reporter &pointer_reporter,
|
|
Input::Session_component &window_layouter_input,
|
|
Decorator_content_callback &content_callback)
|
|
:
|
|
_ram(ram),
|
|
_pointer_reporter(pointer_reporter),
|
|
_window_layouter_input(window_layouter_input),
|
|
_content_callback(content_callback),
|
|
_ep(ep), _md_alloc(md_alloc)
|
|
{
|
|
_nitpicker_input.sigh(_input_dispatcher);
|
|
}
|
|
|
|
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();
|
|
|
|
/* we trust nitpicker to return a valid number of events */
|
|
|
|
for (size_t i = 0; i < num_events; i++) {
|
|
|
|
Input::Event const &ev = events[i];
|
|
|
|
if (ev.type() == Input::Event::MOTION) {
|
|
Local_reporter::Xml_generator xml(_pointer_reporter, [&] ()
|
|
{
|
|
xml.attribute("xpos", ev.ax());
|
|
xml.attribute("ypos", ev.ay());
|
|
});
|
|
}
|
|
|
|
if (ev.type() == Input::Event::LEAVE) {
|
|
|
|
Local_reporter::Xml_generator xml(_pointer_reporter, [&] ()
|
|
{
|
|
/* report empty pointer model */
|
|
});
|
|
}
|
|
|
|
_window_layouter_input.submit(ev);
|
|
}
|
|
}
|
|
}
|
|
|
|
void _execute_command(Command const &cmd)
|
|
{
|
|
switch (cmd.opcode) {
|
|
|
|
case Command::OP_TITLE:
|
|
{
|
|
unsigned long id = 0;
|
|
Genode::ascii_to(cmd.title.title.string(), id);
|
|
|
|
if (id > 0)
|
|
_content_registry.insert(cmd.title.view,
|
|
Window_registry::Id(id));
|
|
return;
|
|
}
|
|
|
|
case Command::OP_TO_FRONT:
|
|
|
|
try {
|
|
|
|
/*
|
|
* If the content view is re-stacked, replace it by the real
|
|
* window content.
|
|
*
|
|
* The lookup fails with an exception for non-content views.
|
|
* In this case, forward the command.
|
|
*/
|
|
Window_registry::Id win_id = _content_registry.lookup(cmd.to_front.view);
|
|
|
|
/*
|
|
* Replace content view originally created by the decorator
|
|
* by view that shows the real window content.
|
|
*/
|
|
Nitpicker::View_capability view_cap =
|
|
_content_callback.content_view(win_id);
|
|
|
|
_nitpicker_session.view_handle(view_cap,
|
|
cmd.to_front.view);
|
|
|
|
_nitpicker_session.enqueue(cmd);
|
|
_nitpicker_session.execute();
|
|
|
|
/*
|
|
* Now that the physical content view exists, it is time
|
|
* to revisit the child views.
|
|
*/
|
|
_content_callback.update_content_child_views(win_id);
|
|
|
|
} catch (Decorator_content_registry::Lookup_failed) {
|
|
|
|
_nitpicker_session.enqueue(cmd);
|
|
}
|
|
|
|
return;
|
|
|
|
case Command::OP_GEOMETRY:
|
|
|
|
try {
|
|
|
|
/*
|
|
* If the content view changes position, propagate the new
|
|
* position to the nitpicker service to properly transform
|
|
* absolute input coordinates.
|
|
*/
|
|
Window_registry::Id win_id = _content_registry.lookup(cmd.geometry.view);
|
|
|
|
_content_callback.content_geometry(win_id, cmd.geometry.rect);
|
|
}
|
|
catch (Decorator_content_registry::Lookup_failed) { }
|
|
|
|
/* forward command */
|
|
_nitpicker_session.enqueue(cmd);
|
|
return;
|
|
|
|
case Command::OP_OFFSET:
|
|
|
|
try {
|
|
|
|
/*
|
|
* If non-content views change their offset (if the lookup
|
|
* fails), propagate the event
|
|
*/
|
|
_content_registry.lookup(cmd.geometry.view);
|
|
}
|
|
catch (Decorator_content_registry::Lookup_failed) {
|
|
_nitpicker_session.enqueue(cmd);
|
|
}
|
|
return;
|
|
|
|
case Command::OP_TO_BACK:
|
|
case Command::OP_BACKGROUND:
|
|
case Command::OP_NOP:
|
|
|
|
_nitpicker_session.enqueue(cmd);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void upgrade(const char *args)
|
|
{
|
|
Genode::env()->parent()->upgrade(_nitpicker_session, args);
|
|
}
|
|
|
|
|
|
/*********************************
|
|
** Nitpicker session interface **
|
|
*********************************/
|
|
|
|
Framebuffer::Session_capability framebuffer_session() override
|
|
{
|
|
return _nitpicker_session.framebuffer_session();
|
|
}
|
|
|
|
Input::Session_capability input_session() override
|
|
{
|
|
/*
|
|
* Deny input to the decorator. User input referring to the
|
|
* window decorations is routed to the window manager.
|
|
*/
|
|
return Input::Session_capability();
|
|
}
|
|
|
|
View_handle create_view(View_handle parent) override
|
|
{
|
|
return _nitpicker_session.create_view(parent);
|
|
}
|
|
|
|
void destroy_view(View_handle view) override
|
|
{
|
|
_nitpicker_session.destroy_view(view);
|
|
}
|
|
|
|
View_handle view_handle(View_capability view_cap, View_handle handle) override
|
|
{
|
|
return _nitpicker_session.view_handle(view_cap, handle);
|
|
}
|
|
|
|
View_capability view_capability(View_handle view) override
|
|
{
|
|
return _nitpicker_session.view_capability(view);
|
|
}
|
|
|
|
void release_view_handle(View_handle view) override
|
|
{
|
|
/* XXX dealloc View_ptr */
|
|
_nitpicker_session.release_view_handle(view);
|
|
}
|
|
|
|
Genode::Dataspace_capability command_dataspace() override
|
|
{
|
|
return _command_ds.cap();
|
|
}
|
|
|
|
void execute() override
|
|
{
|
|
for (unsigned i = 0; i < _command_buffer.num(); i++) {
|
|
try {
|
|
_execute_command(_command_buffer.get(i));
|
|
}
|
|
catch (...) {
|
|
PWRN("unhandled exception while processing command from decorator");
|
|
}
|
|
}
|
|
_nitpicker_session.execute();
|
|
}
|
|
|
|
Framebuffer::Mode mode() override
|
|
{
|
|
return _nitpicker_session.mode();
|
|
}
|
|
|
|
void mode_sigh(Genode::Signal_context_capability sigh) override
|
|
{
|
|
_nitpicker_session.mode_sigh(sigh);
|
|
}
|
|
|
|
void buffer(Framebuffer::Mode mode, bool use_alpha) override
|
|
{
|
|
_nitpicker_session.buffer(mode, use_alpha);
|
|
}
|
|
|
|
void focus(Genode::Capability<Nitpicker::Session>) { }
|
|
};
|
|
|
|
|
|
struct Wm::Decorator_nitpicker_service : Genode::Service, Genode::Noncopyable
|
|
{
|
|
private:
|
|
|
|
Entrypoint &_ep;
|
|
Allocator &_md_alloc;
|
|
Ram_session_capability _ram;
|
|
Local_reporter &_pointer_reporter;
|
|
Input::Session_component &_window_layouter_input;
|
|
Decorator_content_callback &_content_callback;
|
|
|
|
|
|
public:
|
|
|
|
Decorator_nitpicker_service(Entrypoint &ep, Allocator &md_alloc,
|
|
Ram_session_capability ram,
|
|
Local_reporter &pointer_reporter,
|
|
Input::Session_component &window_layouter_input,
|
|
Decorator_content_callback &content_callback)
|
|
:
|
|
Service("Nitpicker"),
|
|
_ep(ep), _md_alloc(md_alloc),
|
|
_ram(ram), _pointer_reporter(pointer_reporter),
|
|
_window_layouter_input(window_layouter_input),
|
|
_content_callback(content_callback)
|
|
{ }
|
|
|
|
Genode::Session_capability
|
|
session(const char *, Genode::Affinity const &) override
|
|
{
|
|
Decorator_nitpicker_session *s = new (_md_alloc)
|
|
Decorator_nitpicker_session(_ram, _ep, _md_alloc,
|
|
_pointer_reporter,
|
|
_window_layouter_input,
|
|
_content_callback);
|
|
|
|
return _ep.manage(*s);
|
|
}
|
|
|
|
void upgrade(Genode::Session_capability session, const char *args) override
|
|
{
|
|
typedef typename Object_pool<Decorator_nitpicker_session>::Guard Object_guard;
|
|
Object_guard np_session(_ep.rpc_ep().lookup_and_lock(session));
|
|
|
|
if (np_session)
|
|
np_session->upgrade(args);
|
|
}
|
|
|
|
void close(Genode::Session_capability) { }
|
|
};
|
|
|
|
#endif /* _DECORATOR_NITPICKER_H_ */
|