/* * \brief Virtualized nitpicker service announced to the outside world * \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 _NITPICKER_H_ #define _NITPICKER_H_ /* Genode includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* local includes */ #include #include #include #include namespace Wm { using Genode::Rpc_object; using Genode::List; using Genode::Allocator; using Genode::Affinity; using Genode::static_cap_cast; using Genode::Signal_rpc_member; using Genode::Ram_session_capability; using Genode::Weak_ptr; using Genode::Locked_ptr; using Genode::Tslab; using Genode::Attached_ram_dataspace; using Genode::Signal_context_capability; using Genode::Signal_transmitter; using Genode::Reporter; using Genode::Capability; } namespace Wm { namespace Nitpicker { using namespace ::Nitpicker; class Click_handler; class View_handle_ctx; class View; class Top_level_view; class Child_view; class Session_control_fn; class Session_component; class Root; typedef Genode::Surface_base::Rect Rect; typedef Genode::Surface_base::Point Point; typedef Genode::Session_label Session_label; } } /** * Interface used for propagating clicks into unfocused windows to the layouter * * The click handler is invoked only for those click events that are of * interest to the layouter. In particular, a click into an unfocused window * may trigger the layouter to raise the window and change the focus. However, * clicks into an already focused window should be of no interest to the * layouter. So we hide them from the layouter. */ struct Wm::Nitpicker::Click_handler { virtual void handle_click(Point pos) = 0; virtual void handle_enter(Point pos) = 0; }; struct Nitpicker::View { GENODE_RPC_INTERFACE(); }; class Wm::Nitpicker::View : public Genode::Weak_object, public Genode::Rpc_object< ::Nitpicker::View> { protected: typedef Genode::String<100> Title; typedef Nitpicker::Session::Command Command; typedef Nitpicker::Session::View_handle View_handle; Session_label _session_label; Nitpicker::Session_client &_real_nitpicker; View_handle _real_handle; Title _title; Rect _geometry; Point _buffer_offset; Weak_ptr _neighbor_ptr; bool _neighbor_behind; bool _has_alpha; View(Nitpicker::Session_client &real_nitpicker, Session_label const &session_label, bool has_alpha) : _session_label(session_label), _real_nitpicker(real_nitpicker), _has_alpha(has_alpha) { } /** * Propagate cached view geometry to the physical nitpicker view */ virtual void _propagate_view_geometry() = 0; /** * Apply cached view state to the physical nitpicker view */ void _apply_view_config() { if (!_real_handle.valid()) return; _propagate_view_geometry(); _real_nitpicker.enqueue(_real_handle, _buffer_offset); _real_nitpicker.enqueue (_real_handle, _title.string()); View_handle real_neighbor_handle; Locked_ptr neighbor(_neighbor_ptr); if (neighbor.is_valid()) real_neighbor_handle = _real_nitpicker.view_handle(neighbor->real_view_cap()); if (_neighbor_behind) _real_nitpicker.enqueue(_real_handle, real_neighbor_handle); else _real_nitpicker.enqueue(_real_handle, real_neighbor_handle); _real_nitpicker.execute(); if (real_neighbor_handle.valid()) _real_nitpicker.release_view_handle(real_neighbor_handle); } public: ~View() { if (_real_handle.valid()) _real_nitpicker.destroy_view(_real_handle); } Point virtual_position() const { return _geometry.p1(); } virtual bool belongs_to_win_id(Window_registry::Id id) const = 0; virtual void geometry(Rect geometry) { _geometry = geometry; /* * Propagate new size to real nitpicker view but */ if (_real_handle.valid()) { _propagate_view_geometry(); _real_nitpicker.execute(); } } virtual void title(char const *title) { _title = Title(title); if (_real_handle.valid()) { _real_nitpicker.enqueue(_real_handle, title); _real_nitpicker.execute(); } } virtual Point input_anchor_position() const = 0; virtual void stack(Weak_ptr neighbor_ptr, bool behind) { } View_handle real_handle() const { return _real_handle; } View_capability real_view_cap() { return _real_nitpicker.view_capability(_real_handle); } void buffer_offset(Point buffer_offset) { _buffer_offset = buffer_offset; if (_real_handle.valid()) { _real_nitpicker.enqueue(_real_handle, _buffer_offset); _real_nitpicker.execute(); } } bool has_alpha() const { return _has_alpha; } }; class Wm::Nitpicker::Top_level_view : public View, public List::Element { private: Window_registry::Id _win_id; Window_registry &_window_registry; /* * Geometry of window-content view, which corresponds to the location * of the window content as known by the decorator. */ Rect _content_geometry; bool _resizeable = false; Title _window_title; Session_label _session_label; typedef Nitpicker::Session::Command Command; public: Top_level_view(Nitpicker::Session_client &real_nitpicker, Session_label const &session_label, bool has_alpha, Window_registry &window_registry) : View(real_nitpicker, session_label, has_alpha), _window_registry(window_registry), _session_label(session_label) { } ~Top_level_view() { if (_win_id.valid()) _window_registry.destroy(_win_id); View::lock_for_destruction(); } void _propagate_view_geometry() override { } void geometry(Rect geometry) override { /* * Add window to the window-list model on the first call. We * defer the creation of the window ID until the time when the * initial geometry is known. */ if (!_win_id.valid()) { _win_id = _window_registry.create(); _window_registry.title(_win_id, _window_title.string()); _window_registry.label(_win_id, _session_label); _window_registry.has_alpha(_win_id, View::has_alpha()); _window_registry.resizeable(_win_id, _resizeable); } _window_registry.size(_win_id, geometry.area()); View::geometry(geometry); } Area size() const { return _geometry.area(); } void title(char const *title) override { View::title(title); _window_title = Title(title); if (_win_id.valid()) _window_registry.title(_win_id, _window_title.string()); } bool has_win_id(Window_registry::Id id) const { return id == _win_id; } bool belongs_to_win_id(Window_registry::Id id) const override { return has_win_id(id); } Point input_anchor_position() const override { return _content_geometry.p1(); } void content_geometry(Rect rect) { _content_geometry = rect; } View_capability content_view() { if (!_real_handle.valid()) { /* * Create and configure physical nitpicker view. */ _real_handle = _real_nitpicker.create_view(); _real_nitpicker.enqueue(_real_handle, _buffer_offset); _real_nitpicker.enqueue (_real_handle, _title.string()); _real_nitpicker.execute(); } return _real_nitpicker.view_capability(_real_handle); } void is_hidden(bool is_hidden) { _window_registry.is_hidden(_win_id, is_hidden); } void resizeable(bool resizeable) { _resizeable = resizeable; if (_win_id.valid()) _window_registry.resizeable(_win_id, resizeable); } }; class Wm::Nitpicker::Child_view : public View, public List::Element { private: Weak_ptr mutable _parent; public: Child_view(Nitpicker::Session_client &real_nitpicker, Session_label const &session_label, bool has_alpha, Weak_ptr parent) : View(real_nitpicker, session_label, has_alpha), _parent(parent) { try_to_init_real_view(); } ~Child_view() { View::lock_for_destruction(); } void _propagate_view_geometry() override { _real_nitpicker.enqueue(_real_handle, _geometry); } void stack(Weak_ptr neighbor_ptr, bool behind) override { _neighbor_ptr = neighbor_ptr; _neighbor_behind = behind; _apply_view_config(); } bool belongs_to_win_id(Window_registry::Id id) const override { Locked_ptr parent(_parent); return parent.is_valid() && parent->belongs_to_win_id(id); } Point input_anchor_position() const override { Locked_ptr parent(_parent); if (parent.is_valid()) return parent->input_anchor_position(); return Point(); } void try_to_init_real_view() { if (_real_handle.valid()) return; Locked_ptr parent(_parent); if (!parent.is_valid()) return; View_handle parent_handle = _real_nitpicker.view_handle(parent->real_view_cap()); if (!parent_handle.valid()) return; _real_handle = _real_nitpicker.create_view(parent_handle); _real_nitpicker.release_view_handle(parent_handle); _apply_view_config(); } void update_child_stacking() { _apply_view_config(); } }; struct Wm::Nitpicker::Session_control_fn { virtual void session_control(char const *selector, Session::Session_control) = 0; }; class Wm::Nitpicker::Session_component : public Rpc_object, public List::Element { private: typedef Nitpicker::Session::View_handle View_handle; Session_label _session_label; Ram_session_client _ram; Nitpicker::Connection _session { _session_label.string() }; Window_registry &_window_registry; Session_control_fn &_session_control_fn; Entrypoint &_ep; Tslab _top_level_view_alloc; Tslab _child_view_alloc; List _top_level_views; List _child_views; Input::Session_component _input_session; Input::Session_capability _input_session_cap; Click_handler &_click_handler; Signal_context_capability _mode_sigh; Area _requested_size; bool _resize_requested = false; bool _has_alpha = false; /* * Command buffer */ typedef Nitpicker::Session::Command_buffer Command_buffer; Attached_ram_dataspace _command_ds { &_ram, sizeof(Command_buffer) }; Command_buffer &_command_buffer = *_command_ds.local_addr(); /* * View handle registry */ typedef Genode::Handle_registry View_handle_registry; View_handle_registry _view_handle_registry; /* * Input */ Input::Session_client _nitpicker_input { _session.input_session() }; Attached_dataspace _nitpicker_input_ds { _nitpicker_input.dataspace() }; Signal_rpc_member _input_dispatcher { _ep, *this, &Session_component::_input_handler }; Point _input_origin() const { if (Top_level_view const *v = _top_level_views.first()) return v->virtual_position() - v->input_anchor_position(); if (Child_view const *v = _child_views.first()) return Point(0, 0) - v->input_anchor_position(); return Point(); } /** * Translate input event to the client's coordinate system */ Input::Event _translate_event(Input::Event const ev, Point const input_origin) { switch (ev.type()) { case Input::Event::MOTION: case Input::Event::PRESS: case Input::Event::RELEASE: case Input::Event::FOCUS: case Input::Event::LEAVE: { Point abs_pos = Point(ev.ax(), ev.ay()) + input_origin; return Input::Event(ev.type(), ev.code(), abs_pos.x(), abs_pos.y(), 0, 0); } case Input::Event::TOUCH: case Input::Event::WHEEL: { Point abs_pos = Point(ev.ax(), ev.ay()) + input_origin; return Input::Event(ev.type(), ev.code(), abs_pos.x(), abs_pos.y(), ev.rx(), ev.ry()); } case Input::Event::INVALID: return ev; } return Input::Event(); } bool _is_click_into_unfocused_view(Input::Event const ev) { /* * XXX check if unfocused * * Right now, we report more butten events to the layouter * than the layouter really needs. */ if (ev.type() == Input::Event::PRESS && ev.keycode() == Input::BTN_LEFT) return true; return false; } bool _first_motion = true; void _input_handler(unsigned) { Point const input_origin = _input_origin(); Input::Event const * const events = _nitpicker_input_ds.local_addr(); 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]; /* propagate layout-affecting events to the layouter */ if (_is_click_into_unfocused_view(ev)) _click_handler.handle_click(Point(ev.ax(), ev.ay())); /* * Reset pointer model for the decorator once the pointer * enters the application area of a window. */ if (ev.type() == Input::Event::MOTION && _first_motion) { _click_handler.handle_enter(Point(ev.ax(), ev.ay())); _first_motion = false; } if (ev.type() == Input::Event::LEAVE) _first_motion = true; /* submit event to the client */ _input_session.submit(_translate_event(ev, input_origin)); } } } View &_create_view_object(View_handle parent_handle) { /* * Create child view */ if (parent_handle.valid()) { Weak_ptr parent_ptr = _view_handle_registry.lookup(parent_handle); Child_view *view = new (_child_view_alloc) Child_view(_session, _session_label, _has_alpha, parent_ptr); _child_views.insert(view); return *view; } /* * Create top-level view */ else { Top_level_view *view = new (_top_level_view_alloc) Top_level_view(_session, _session_label, _has_alpha, _window_registry); view->resizeable(_mode_sigh.valid()); _top_level_views.insert(view); return *view; } } void _destroy_top_level_view(Top_level_view &view) { _top_level_views.remove(&view); _ep.dissolve(view); Genode::destroy(&_top_level_view_alloc, &view); } void _destroy_child_view(Child_view &view) { _child_views.remove(&view); _ep.dissolve(view); Genode::destroy(&_child_view_alloc, &view); } void _destroy_view_object(View &view) { if (Top_level_view *v = dynamic_cast(&view)) _destroy_top_level_view(*v); if (Child_view *v = dynamic_cast(&view)) _destroy_child_view(*v); } void _execute_command(Command const &command) { switch (command.opcode) { case Command::OP_GEOMETRY: { Locked_ptr view(_view_handle_registry.lookup(command.geometry.view)); if (view.is_valid()) view->geometry(command.geometry.rect); return; } case Command::OP_OFFSET: { Locked_ptr view(_view_handle_registry.lookup(command.offset.view)); if (view.is_valid()) view->buffer_offset(command.offset.offset); return; } case Command::OP_TO_FRONT: { Locked_ptr view(_view_handle_registry.lookup(command.to_front.view)); if (!view.is_valid()) return; /* bring to front if no neighbor is specified */ if (!command.to_front.neighbor.valid()) { view->stack(Weak_ptr(), true); return; } /* stack view relative to neighbor */ view->stack(_view_handle_registry.lookup(command.to_front.neighbor), true); return; } case Command::OP_TO_BACK: { return; } case Command::OP_BACKGROUND: { return; } case Command::OP_TITLE: { char sanitized_title[command.title.title.capacity()]; Genode::strncpy(sanitized_title, command.title.title.string(), sizeof(sanitized_title)); for (char *c = sanitized_title; *c; c++) if (*c == '"') *c = '\''; Locked_ptr view(_view_handle_registry.lookup(command.title.view)); if (view.is_valid()) view->title(sanitized_title); return; } case Command::OP_NOP: return; } } public: /** * Constructor * * \param ep entrypoint used for managing the views */ Session_component(Ram_session_capability ram, Window_registry &window_registry, Entrypoint &ep, Allocator &session_alloc, Session_label const &session_label, Click_handler &click_handler, Session_control_fn &session_control_fn) : _session_label(session_label), _ram(ram), _window_registry(window_registry), _session_control_fn(session_control_fn), _ep(ep), _top_level_view_alloc(&session_alloc), _child_view_alloc(&session_alloc), _input_session_cap(_ep.manage(_input_session)), _click_handler(click_handler), _view_handle_registry(session_alloc) { _nitpicker_input.sigh(_input_dispatcher); _input_session.event_queue().enabled(true); } ~Session_component() { while (Top_level_view *view = _top_level_views.first()) _destroy_view_object(*view); while (Child_view *view = _child_views.first()) _destroy_view_object(*view); _ep.dissolve(_input_session); } void upgrade(char const *args) { Genode::env()->parent()->upgrade(_session, args); } void try_to_init_real_child_views() { for (Child_view *v = _child_views.first(); v; v = v->next()) v->try_to_init_real_view(); } void update_stacking_order_of_children(Window_registry::Id id) { for (Child_view *v = _child_views.first(); v; v = v->next()) if (v->belongs_to_win_id(id)) v->update_child_stacking(); } void content_geometry(Window_registry::Id id, Rect rect) { for (Top_level_view *v = _top_level_views.first(); v; v = v->next()) { if (!v->has_win_id(id)) continue; v->content_geometry(rect); break; } } View_capability content_view(Window_registry::Id id) { for (Top_level_view *v = _top_level_views.first(); v; v = v->next()) if (v->has_win_id(id.value)) return v->content_view(); return View_capability(); } bool has_win_id(unsigned id) const { for (Top_level_view const *v = _top_level_views.first(); v; v = v->next()) if (v->has_win_id(id)) return true; return false; } Session_label session_label() const { return _session_label; } bool matches_session_label(char const *selector) const { /* * Append label separator to match selectors with a trailing * separator. * * The code originates from nitpicker's 'session.h'. */ char label[Genode::Session_label::capacity() + 4]; Genode::snprintf(label, sizeof(label), "%s ->", _session_label.string()); return Genode::strcmp(label, selector, Genode::strlen(selector)) == 0; } void request_resize(Area size) { _requested_size = size; _resize_requested = true; /* notify client */ if (_mode_sigh.valid()) Signal_transmitter(_mode_sigh).submit(); } void is_hidden(bool is_hidden) { for (Top_level_view *v = _top_level_views.first(); v; v = v->next()) v->is_hidden(is_hidden); } /** * Return session capability to real nitpicker session */ Capability session() { return _session; } /********************************* ** Nitpicker session interface ** *********************************/ Framebuffer::Session_capability framebuffer_session() override { return _session.framebuffer_session(); } Input::Session_capability input_session() override { return _input_session_cap; } View_handle create_view(View_handle parent) override { try { View &view = _create_view_object(parent); _ep.manage(view); return _view_handle_registry.alloc(view); } catch (View_handle_registry::Lookup_failed) { return View_handle(); } } void destroy_view(View_handle handle) override { try { Locked_ptr view(_view_handle_registry.lookup(handle)); if (view.is_valid()) _destroy_view_object(*view); } catch (View_handle_registry::Lookup_failed) { } _view_handle_registry.free(handle); } View_handle view_handle(View_capability view_cap, View_handle handle) override { return _ep.rpc_ep().apply(view_cap, [&] (View *view) { return (view) ? _view_handle_registry.alloc(*view, handle) : View_handle(); }); } View_capability view_capability(View_handle handle) override { Locked_ptr view(_view_handle_registry.lookup(handle)); return view.is_valid() ? view->cap() : View_capability(); } void release_view_handle(View_handle handle) override { try { _view_handle_registry.free(handle); } catch (View_handle_registry::Lookup_failed) { PWRN("view lookup failed while releasing view handle"); return; } } 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 (View_handle_registry::Lookup_failed) { PWRN("view lookup failed during command execution"); } } /* propagate window-list changes to the layouter */ _window_registry.flush(); } Framebuffer::Mode mode() override { Framebuffer::Mode const real_mode = _session.mode(); /* * While resizing the window, return requested window size as * mode */ if (_resize_requested) return Framebuffer::Mode(_requested_size.w(), _requested_size.h(), real_mode.format()); /* * If the first top-level view has a defined size, use it * as the size of the virtualized nitpicker session. */ if (Top_level_view const *v = _top_level_views.first()) if (v->size().valid()) return Framebuffer::Mode(v->size().w(), v->size().h(), real_mode.format()); /* * If top-level view has yet been defined, return the real mode. */ return real_mode; } void mode_sigh(Genode::Signal_context_capability sigh) override { _mode_sigh = sigh; /* * We consider a window as resizable if the client shows interest * in mode-change notifications. */ bool const resizeable = _mode_sigh.valid(); for (Top_level_view *v = _top_level_views.first(); v; v = v->next()) v->resizeable(resizeable); } void buffer(Framebuffer::Mode mode, bool has_alpha) override { /* * We must not perform the 'buffer' operation on the connection * object because the 'Nitpicker::Connection::buffer' * implementation implicitly performs upgrade operations. * * Here, we merely want to forward the buffer RPC call to the * wrapped nitpicker session. Otherwise, we would perform * session upgrades initiated by the wm client's buffer * operation twice. */ Nitpicker::Session_client(_session.cap()).buffer(mode, has_alpha); _has_alpha = has_alpha; } void focus(Genode::Capability) { } void session_control(Label suffix, Session_control operation) override { /* * Append label argument to session label * * The code originates from nitpicker's 'main.cc'. */ char selector[Label::size()]; Genode::snprintf(selector, sizeof(selector), "%s%s%s", _session_label.string(), suffix.length() ? " -> " : "", suffix.string()); _session_control_fn.session_control(selector, operation); } }; class Wm::Nitpicker::Root : public Genode::Rpc_object >, public Decorator_content_callback, public Session_control_fn { private: Entrypoint &_ep; Allocator &_md_alloc; Ram_session_capability _ram; enum { STACK_SIZE = 1024*sizeof(long) }; Reporter &_pointer_reporter; Reporter &_focus_request_reporter; unsigned _focus_request_cnt = 0; Last_motion _last_motion = LAST_MOTION_DECORATOR; Window_registry &_window_registry; Input::Session_component _window_layouter_input; Input::Session_capability _window_layouter_input_cap { _ep.manage(_window_layouter_input) }; /* handler that forwards clicks into unfocused windows to the layouter */ struct Click_handler : Nitpicker::Click_handler { Input::Session_component &window_layouter_input; Reporter &pointer_reporter; Last_motion &last_motion; void _submit_button_event(Input::Event::Type type, Nitpicker::Point pos) { window_layouter_input.submit(Input::Event(type, Input::BTN_LEFT, pos.x(), pos.y(), 0, 0)); } void handle_enter(Nitpicker::Point pos) override { last_motion = LAST_MOTION_NITPICKER; Reporter::Xml_generator xml(pointer_reporter, [&] () { xml.attribute("xpos", pos.x()); xml.attribute("ypos", pos.y()); }); } void handle_click(Nitpicker::Point pos) override { /* * Propagate clicked-at position to decorator such that it can * update its hover model. */ Reporter::Xml_generator xml(pointer_reporter, [&] () { xml.attribute("xpos", pos.x()); xml.attribute("ypos", pos.y()); }); /* * Supply artificial mouse click to the decorator's input session * (which is routed to the layouter). */ _submit_button_event(Input::Event::PRESS, pos); _submit_button_event(Input::Event::RELEASE, pos); } Click_handler(Input::Session_component &window_layouter_input, Reporter &pointer_reporter, Last_motion &last_motion) : window_layouter_input(window_layouter_input), pointer_reporter(pointer_reporter), last_motion(last_motion) { } } _click_handler { _window_layouter_input, _pointer_reporter, _last_motion }; /** * List of regular sessions */ List _sessions; Layouter_nitpicker_session *_layouter_session = nullptr; List _decorator_sessions; /** * Nitpicker session used to perform session-control operations */ Nitpicker::Session &_focus_nitpicker_session; public: /** * Constructor */ Root(Entrypoint &ep, Window_registry &window_registry, Allocator &md_alloc, Ram_session_capability ram, Reporter &pointer_reporter, Reporter &focus_request_reporter, Nitpicker::Session &focus_nitpicker_session) : _ep(ep), _md_alloc(md_alloc), _ram(ram), _pointer_reporter(pointer_reporter), _focus_request_reporter(focus_request_reporter), _window_registry(window_registry), _focus_nitpicker_session(focus_nitpicker_session) { _window_layouter_input.event_queue().enabled(true); Genode::env()->parent()->announce(_ep.manage(*this)); } /******************** ** Root interface ** ********************/ Genode::Session_capability session(Session_args const &args, Affinity const &affinity) override { Genode::Session_label session_label(args.string()); enum Role { ROLE_DECORATOR, ROLE_LAYOUTER, ROLE_REGULAR, ROLE_DIRECT }; Role role = ROLE_REGULAR; /* * Determine session policy */ try { Genode::Xml_node policy = Genode::Session_policy(session_label); char const *role_attr = "role"; if (policy.has_attribute(role_attr)) { if (policy.attribute(role_attr).has_value("layouter")) role = ROLE_LAYOUTER; if (policy.attribute(role_attr).has_value("decorator")) role = ROLE_DECORATOR; if (policy.attribute(role_attr).has_value("direct")) role = ROLE_DIRECT; } } catch (...) { } switch (role) { case ROLE_REGULAR: { auto session = new (_md_alloc) Session_component(_ram, _window_registry, _ep, _md_alloc, session_label, _click_handler, *this); _sessions.insert(session); return _ep.manage(*session); } case ROLE_DECORATOR: { auto session = new (_md_alloc) Decorator_nitpicker_session(_ram, _ep, _md_alloc, _pointer_reporter, _last_motion, _window_layouter_input, *this); _decorator_sessions.insert(session); return _ep.manage(*session); } case ROLE_LAYOUTER: { _layouter_session = new (_md_alloc) Layouter_nitpicker_session(*Genode::env()->ram_session(), _window_layouter_input_cap, _focus_nitpicker_session.mode()); return _ep.manage(*_layouter_session); } case ROLE_DIRECT: { Direct_nitpicker_session *session = new (_md_alloc) Direct_nitpicker_session(session_label); return _ep.manage(*session); } } return Session_capability(); } void upgrade(Genode::Session_capability session_cap, Upgrade_args const &args) override { if (!args.is_valid_string()) throw Root::Invalid_args(); auto lambda = [&] (Rpc_object_base *session) { if (!session) { PDBG("session lookup failed"); return; } Session_component *regular_session = dynamic_cast(session); if (regular_session) regular_session->upgrade(args.string()); Decorator_nitpicker_session *decorator_session = dynamic_cast(session); if (decorator_session) decorator_session->upgrade(args.string()); Direct_nitpicker_session *direct_session = dynamic_cast(session); if (direct_session) direct_session->upgrade(args.string()); }; _ep.rpc_ep().apply(session_cap, lambda); } void close(Genode::Session_capability session_cap) override { Genode::Rpc_entrypoint &ep = _ep.rpc_ep(); Session_component *regular_session = ep.apply(session_cap, [this] (Session_component *session) { if (session) { _sessions.remove(session); _ep.dissolve(*session); } return session; }); if (regular_session) { Genode::destroy(_md_alloc, regular_session); return; } Direct_nitpicker_session *direct_session = ep.apply(session_cap, [this] (Direct_nitpicker_session *session) { if (session) { _ep.dissolve(*session); } return session; }); if (direct_session) { Genode::destroy(_md_alloc, direct_session); return; } Decorator_nitpicker_session *decorator_session = ep.apply(session_cap, [this] (Decorator_nitpicker_session *session) { if (session) { _decorator_sessions.remove(session); _ep.dissolve(*session); } return session; }); if (decorator_session) { Genode::destroy(_md_alloc, decorator_session); return; } auto layouter_lambda = [this] (Layouter_nitpicker_session *session) { _ep.dissolve(*_layouter_session); _layouter_session = nullptr; return session; }; if (ep.apply(session_cap, layouter_lambda) == _layouter_session) { Genode::destroy(_md_alloc, _layouter_session); return; } } /********************************** ** Session_control_fn interface ** **********************************/ void session_control(char const *selector, Session::Session_control operation) override { for (Session_component *s = _sessions.first(); s; s = s->next()) { if (!s->matches_session_label(selector)) continue; switch (operation) { case Session::SESSION_CONTROL_SHOW: s->is_hidden(false); break; case Session::SESSION_CONTROL_HIDE: s->is_hidden(true); break; case Session::SESSION_CONTROL_TO_FRONT: /* post focus request to the layouter */ Genode::Reporter::Xml_generator xml(_focus_request_reporter, [&] () { xml.attribute("label", s->session_label().string()); xml.attribute("id", ++_focus_request_cnt); }); break; } } _window_registry.flush(); /* * Forward the request to the nitpicker control session to apply * the show/hide/to-front operations on "direct" nitpicker * sessions. */ _focus_nitpicker_session.session_control(selector, operation); } /****************************************** ** Decorator_content_callback interface ** ******************************************/ /* * This function is called once the decorator has produced the content * view for a new window, or when a window is brought to the front. */ View_capability content_view(Window_registry::Id id) override { /* * Propagate the request to the sessions. It will be picked up * by the session to which the specified window ID belongs. * The real content view will be created as a side effect of * calling 's->content_view'. */ for (Session_component *s = _sessions.first(); s; s = s->next()) if (s->has_win_id(id.value)) return s->content_view(id.value); return View_capability(); } void update_content_child_views(Window_registry::Id id) override { /* * Try to create physical views for its child views. */ for (Session_component *s = _sessions.first(); s; s = s->next()) { s->try_to_init_real_child_views(); } /* * Apply the stacking order to the child views that belong to the * given window ID. I.e., when the window was brought to the front, * we need to restack its child views such that they end up in * front of the top-level view. Otherwise, the top-level view * will obstruct the child views. */ for (Session_component *s = _sessions.first(); s; s = s->next()) s->update_stacking_order_of_children(id); } void content_geometry(Window_registry::Id id, Rect rect) override { for (Session_component *s = _sessions.first(); s; s = s->next()) s->content_geometry(id, rect); } Capability lookup_nitpicker_session(unsigned win_id) { for (Session_component *s = _sessions.first(); s; s = s->next()) if (s->has_win_id(win_id)) return s->session(); return Capability(); } void request_resize(unsigned win_id, Area size) { for (Session_component *s = _sessions.first(); s; s = s->next()) if (s->has_win_id(win_id)) return s->request_resize(size); } }; #endif /* _NITPICKER_H_ */