/* * \brief Nitpicker main program for Genode * \author Norman Feske * \date 2006-08-04 */ /* * Copyright (C) 2006-2013 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* local includes */ #include "input.h" #include "background.h" #include "clip_guard.h" #include "pointer_origin.h" #include "domain_registry.h" namespace Input { class Session_component; } namespace Framebuffer { class Session_component; } namespace Nitpicker { class Session_component; template class Root; struct Main; } using Genode::size_t; using Genode::Allocator; using Genode::Rpc_entrypoint; using Genode::List; using Genode::Pixel_rgb565; using Genode::strcmp; using Genode::config; using Genode::env; using Genode::Arg_string; using Genode::Object_pool; using Genode::Dataspace_capability; using Genode::Session_label; using Genode::Signal_transmitter; using Genode::Signal_context_capability; using Genode::Signal_rpc_member; using Genode::Attached_ram_dataspace; using Genode::Attached_dataspace; using Genode::Weak_ptr; using Genode::Locked_ptr; Framebuffer::Session *tmp_fb; /*************** ** Utilities ** ***************/ static void report_session(Genode::Reporter &reporter, Session *session) { if (!reporter.is_enabled()) return; Genode::Reporter::Xml_generator xml(reporter, [&] () { if (session) { xml.attribute("label", session->label().string()); xml.attribute("domain", session->domain_name().string()); Color const color = session->color(); char buf[32]; Genode::snprintf(buf, sizeof(buf), "#%02x%02x%02x", color.r, color.g, color.b); xml.attribute("color", buf); } }); } static void report_kill_focus(Genode::Reporter &reporter) { if (!reporter.is_enabled()) return; Genode::Reporter::Xml_generator xml(reporter, [&] () { xml.attribute("label", ""); xml.attribute("domain", ""); xml.attribute("color", "#ff4444"); }); } /* * Font initialization */ extern char _binary_default_tff_start; Text_painter::Font default_font(&_binary_default_tff_start); template struct Screen : public Canvas { Screen(PT *base, Area size) : Canvas(base, size) { } }; class Buffer { private: Area _size; Framebuffer::Mode::Format _format; Genode::Attached_ram_dataspace _ram_ds; public: /** * Constructor - allocate and map dataspace for virtual frame buffer * * \throw Ram_session::Alloc_failed * \throw Rm_session::Attach_failed */ Buffer(Area size, Framebuffer::Mode::Format format, Genode::size_t bytes) : _size(size), _format(format), _ram_ds(env()->ram_session(), bytes) { } /** * Accessors */ Genode::Ram_dataspace_capability ds_cap() const { return _ram_ds.cap(); } Area size() const { return _size; } Framebuffer::Mode::Format format() const { return _format; } void *local_addr() const { return _ram_ds.local_addr(); } }; /** * Interface for triggering the re-allocation of a virtual framebuffer * * Used by 'Framebuffer::Session_component', * implemented by 'Nitpicker::Session_component' */ struct Buffer_provider { virtual Buffer *realloc_buffer(Framebuffer::Mode mode, bool use_alpha) = 0; }; template class Chunky_dataspace_texture : public Buffer, public Texture { private: Framebuffer::Mode::Format _format() { return Framebuffer::Mode::RGB565; } /** * Return base address of alpha channel or 0 if no alpha channel exists */ unsigned char *_alpha_base(Area size, bool use_alpha) { if (!use_alpha) return 0; /* alpha values come right after the pixel values */ return (unsigned char *)local_addr() + calc_num_bytes(size, false); } public: /** * Constructor */ Chunky_dataspace_texture(Area size, bool use_alpha) : Buffer(size, _format(), calc_num_bytes(size, use_alpha)), Texture((PT *)local_addr(), _alpha_base(size, use_alpha), size) { } static Genode::size_t calc_num_bytes(Area size, bool use_alpha) { /* * If using an alpha channel, the alpha buffer follows the * pixel buffer. The alpha buffer is followed by an input * mask buffer. Hence, we have to account one byte per * alpha value and one byte for the input mask value. */ Genode::size_t bytes_per_pixel = sizeof(PT) + (use_alpha ? 2 : 0); return bytes_per_pixel*size.w()*size.h(); } unsigned char *input_mask_buffer() { if (!Texture::alpha()) return 0; Area const size = Texture::size(); /* input-mask values come right after the alpha values */ return (unsigned char *)local_addr() + calc_num_bytes(size, false) + size.count(); } }; /*********************** ** Input sub session ** ***********************/ class Input::Session_component : public Genode::Rpc_object { public: enum { MAX_EVENTS = 200 }; static size_t ev_ds_size() { return Genode::align_addr(MAX_EVENTS*sizeof(Event), 12); } private: /* * Exported event buffer dataspace */ Attached_ram_dataspace _ev_ram_ds = { env()->ram_session(), ev_ds_size() }; /* * Local event buffer that is copied * to the exported event buffer when * flush() gets called. */ Event _ev_buf[MAX_EVENTS]; unsigned _num_ev = 0; Signal_context_capability _sigh; public: /** * Wake up client */ void submit_signal() { if (_sigh.valid()) Signal_transmitter(_sigh).submit(); } /** * Enqueue event into local event buffer of the input session */ void submit(const Event *ev) { /* drop event when event buffer is full */ if (_num_ev >= MAX_EVENTS) return; /* insert event into local event buffer */ _ev_buf[_num_ev++] = *ev; submit_signal(); } /***************************** ** Input session interface ** *****************************/ Dataspace_capability dataspace() override { return _ev_ram_ds.cap(); } bool is_pending() const override { return _num_ev > 0; } int flush() override { unsigned ev_cnt; /* copy events from local event buffer to exported buffer */ Event *ev_ds_buf = _ev_ram_ds.local_addr(); for (ev_cnt = 0; ev_cnt < _num_ev; ev_cnt++) ev_ds_buf[ev_cnt] = _ev_buf[ev_cnt]; _num_ev = 0; return ev_cnt; } void sigh(Genode::Signal_context_capability sigh) override { _sigh = sigh; } }; /***************************** ** Framebuffer sub session ** *****************************/ class Framebuffer::Session_component : public Genode::Rpc_object { private: ::Buffer *_buffer = 0; View_stack &_view_stack; ::Session &_session; Framebuffer::Session &_framebuffer; Buffer_provider &_buffer_provider; Signal_context_capability _mode_sigh; Signal_context_capability _sync_sigh; Framebuffer::Mode _mode; bool _alpha = false; public: /** * Constructor */ Session_component(View_stack &view_stack, ::Session &session, Framebuffer::Session &framebuffer, Buffer_provider &buffer_provider) : _view_stack(view_stack), _session(session), _framebuffer(framebuffer), _buffer_provider(buffer_provider) { } /** * Change virtual framebuffer mode * * Called by Nitpicker::Session_component when re-dimensioning the * buffer. * * The new mode does not immediately become active. The client can * keep using an already obtained framebuffer dataspace. However, * we inform the client about the mode change via a signal. If the * client calls 'dataspace' the next time, the new mode becomes * effective. */ void notify_mode_change(Framebuffer::Mode mode, bool alpha) { _mode = mode; _alpha = alpha; if (_mode_sigh.valid()) Signal_transmitter(_mode_sigh).submit(); } void submit_sync() { if (_sync_sigh.valid()) Signal_transmitter(_sync_sigh).submit(); } /************************************ ** Framebuffer::Session interface ** ************************************/ Dataspace_capability dataspace() override { _buffer = _buffer_provider.realloc_buffer(_mode, _alpha); return _buffer ? _buffer->ds_cap() : Genode::Ram_dataspace_capability(); } Mode mode() const override { return _mode; } void mode_sigh(Signal_context_capability sigh) override { _mode_sigh = sigh; } void sync_sigh(Signal_context_capability sigh) override { _sync_sigh = sigh; } void refresh(int x, int y, int w, int h) override { Rect const rect(Point(x, y), Area(w, h)); _view_stack.mark_session_views_as_dirty(_session, rect); } }; /***************************************** ** Implementation of Nitpicker service ** *****************************************/ class Nitpicker::Session_component : public Genode::Rpc_object, public ::Session, public Buffer_provider { private: typedef ::View View; Genode::Allocator_guard _session_alloc; Framebuffer::Session &_framebuffer; /* Framebuffer_session_component */ Framebuffer::Session_component _framebuffer_session_component; /* Input_session_component */ Input::Session_component _input_session_component; /* * Entrypoint that is used for the views, input session, * and framebuffer session. */ Rpc_entrypoint &_ep; View_stack &_view_stack; Mode &_mode; Signal_context_capability _mode_sigh; View &_pointer_origin; List _view_list; Genode::Tslab _view_alloc { &_session_alloc }; /* capabilities for sub sessions */ Framebuffer::Session_capability _framebuffer_session_cap; Input::Session_capability _input_session_cap; bool const _provides_default_bg; /* size of currently allocated virtual framebuffer, in bytes */ size_t _buffer_size = 0; Attached_ram_dataspace _command_ds { env()->ram_session(), sizeof(Command_buffer) }; Command_buffer &_command_buffer = *_command_ds.local_addr(); typedef Genode::Handle_registry View_handle_registry; View_handle_registry _view_handle_registry; Genode::Reporter &_focus_reporter; void _release_buffer() { if (!::Session::texture()) return; typedef Pixel_rgb565 PT; /* retrieve pointer to texture from session */ Chunky_dataspace_texture const *cdt = static_cast const *>(::Session::texture()); ::Session::texture(0, false); ::Session::input_mask(0); destroy(&_session_alloc, const_cast *>(cdt)); _session_alloc.upgrade(_buffer_size); _buffer_size = 0; } /** * Helper for performing sanity checks in OP_TO_FRONT and OP_TO_BACK * * We have to check for the equality of both the specified view and * neighbor. If both arguments refer to the same view, the creation of * locked pointers for both views would result in a deadlock. */ bool _views_are_equal(View_handle v1, View_handle v2) { if (!v1.valid() || !v2.valid()) return false; Weak_ptr v1_ptr = _view_handle_registry.lookup(v1); Weak_ptr v2_ptr = _view_handle_registry.lookup(v2); return v1_ptr == v2_ptr; } bool _focus_change_permitted() const { ::Session * const focused_session = _mode.focused_session(); /* * If no session is focused, we allow any client to assign it. This * is useful for programs such as an initial login window that * should receive input events without prior manual selection via * the mouse. * * In principle, a client could steal the focus during time between * a currently focused session gets closed and before the user * manually picks a new session. However, in practice, the focus * policy during application startup and exit is managed by a * window manager that sits between nitpicker and the application. */ if (!focused_session) return true; /* * Check if the currently focused session label belongs to a * session subordinated to the caller, i.e., it originated from * a child of the caller or from the same process. This is the * case if the first part of the focused session label is * identical to the caller's label. */ char const * const focused_label = focused_session->label().string(); char const * const caller_label = label().string(); return strcmp(focused_label, caller_label, Genode::strlen(caller_label)) == 0; } 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()) return; Point pos = command.geometry.rect.p1(); /* transpose position of top-level views by vertical session offset */ if (view->top_level()) pos = ::Session::phys_pos(pos, _view_stack.size()); if (view.is_valid()) _view_stack.geometry(*view, Rect(pos, command.geometry.rect.area())); return; } case Command::OP_OFFSET: { Locked_ptr view(_view_handle_registry.lookup(command.geometry.view)); if (view.is_valid()) _view_stack.buffer_offset(*view, command.offset.offset); return; } case Command::OP_TO_FRONT: { if (_views_are_equal(command.to_front.view, command.to_front.neighbor)) return; 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.stack(*view, nullptr, true); return; } /* stack view relative to neighbor */ Locked_ptr neighbor(_view_handle_registry.lookup(command.to_front.neighbor)); if (neighbor.is_valid()) _view_stack.stack(*view, &(*neighbor), false); return; } case Command::OP_TO_BACK: { if (_views_are_equal(command.to_front.view, command.to_front.neighbor)) return; Locked_ptr view(_view_handle_registry.lookup(command.to_back.view)); if (!view.is_valid()) return; /* bring to front if no neighbor is specified */ if (!command.to_front.neighbor.valid()) { _view_stack.stack(*view, nullptr, false); return; } /* stack view relative to neighbor */ Locked_ptr neighbor(_view_handle_registry.lookup(command.to_back.neighbor)); if (neighbor.is_valid()) _view_stack.stack(*view, &(*neighbor), true); return; } case Command::OP_BACKGROUND: { if (_provides_default_bg) { Locked_ptr view(_view_handle_registry.lookup(command.to_front.view)); if (!view.is_valid()) return; view->background(true); _view_stack.default_background(*view); return; } /* revert old background view to normal mode */ if (::Session::background()) ::Session::background()->background(false); /* assign session background */ Locked_ptr view(_view_handle_registry.lookup(command.to_front.view)); if (!view.is_valid()) return; ::Session::background(&(*view)); /* switch background view to background mode */ if (::Session::background()) view->background(true); return; } case Command::OP_TITLE: { Locked_ptr view(_view_handle_registry.lookup(command.title.view)); if (view.is_valid()) _view_stack.title(*view, command.title.title.string()); return; } case Command::OP_NOP: return; } } void _destroy_view(View &view) { _view_stack.remove_view(view); _ep.dissolve(&view); _view_list.remove(&view); destroy(_view_alloc, &view); } public: /** * Constructor */ Session_component(Session_label const &label, View_stack &view_stack, Mode &mode, View &pointer_origin, Rpc_entrypoint &ep, Framebuffer::Session &framebuffer, bool provides_default_bg, Allocator &session_alloc, size_t ram_quota, Genode::Reporter &focus_reporter) : ::Session(label), _session_alloc(&session_alloc, ram_quota), _framebuffer(framebuffer), _framebuffer_session_component(view_stack, *this, framebuffer, *this), _ep(ep), _view_stack(view_stack), _mode(mode), _pointer_origin(pointer_origin), _framebuffer_session_cap(_ep.manage(&_framebuffer_session_component)), _input_session_cap(_ep.manage(&_input_session_component)), _provides_default_bg(provides_default_bg), _view_handle_registry(_session_alloc), _focus_reporter(focus_reporter) { _session_alloc.upgrade(ram_quota); } /** * Destructor */ ~Session_component() { _ep.dissolve(&_framebuffer_session_component); _ep.dissolve(&_input_session_component); destroy_all_views(); _release_buffer(); } void destroy_all_views() { while (Session_view_list_elem *v = _view_list.first()) _destroy_view(*static_cast(v)); } /** * Deliver mode-change signal to client */ void notify_mode_change() { if (_mode_sigh.valid()) Signal_transmitter(_mode_sigh).submit(); } void upgrade_ram_quota(size_t ram_quota) { _session_alloc.upgrade(ram_quota); } /********************************** ** Nitpicker-internal interface ** **********************************/ void submit_input_event(Input::Event e) { using namespace Input; Point const origin_offset = ::Session::phys_pos(Point(0, 0), _view_stack.size()); /* * Transpose absolute coordinates by session-specific vertical * offset. */ if (e.ax() || e.ay()) e = Event(e.type(), e.code(), Genode::max(0, e.ax() - origin_offset.x()), Genode::max(0, e.ay() - origin_offset.y()), e.rx(), e.ry()); _input_session_component.submit(&e); } void submit_sync() override { _framebuffer_session_component.submit_sync(); } /********************************* ** Nitpicker session interface ** *********************************/ Framebuffer::Session_capability framebuffer_session() override { return _framebuffer_session_cap; } Input::Session_capability input_session() override { return _input_session_cap; } View_handle create_view(View_handle parent_handle) override { View *view = nullptr; /* * Create child view */ if (parent_handle.valid()) { try { Locked_ptr parent(_view_handle_registry.lookup(parent_handle)); if (!parent.is_valid()) return View_handle(); view = new (_view_alloc) View(*this, View::NOT_TRANSPARENT, View::NOT_BACKGROUND, &(*parent)); parent->add_child(*view); } catch (View_handle_registry::Lookup_failed) { return View_handle(); } catch (Genode::Allocator::Out_of_memory) { throw Nitpicker::Session::Out_of_metadata(); } } /* * Create top-level view */ else { try { view = new (_view_alloc) View(*this, View::NOT_TRANSPARENT, View::NOT_BACKGROUND, nullptr); } catch (Genode::Allocator::Out_of_memory) { throw Nitpicker::Session::Out_of_metadata(); } } view->apply_origin_policy(_pointer_origin); _view_list.insert(view); _ep.manage(view); return _view_handle_registry.alloc(*view); } void destroy_view(View_handle handle) override { try { Locked_ptr view(_view_handle_registry.lookup(handle)); if (view.is_valid()) _destroy_view(*view); } catch (View_handle_registry::Lookup_failed) { } _view_handle_registry.free(handle); } View_handle view_handle(View_capability view_cap, View_handle handle) override { View *view = dynamic_cast(_ep.lookup_and_lock(view_cap)); if (!view) return View_handle(); Object_pool::Guard guard(view); return _view_handle_registry.alloc(*view, handle); } View_capability view_capability(View_handle handle) override { try { Locked_ptr view(_view_handle_registry.lookup(handle)); return view.is_valid() ? view->cap() : View_capability(); } catch (View_handle_registry::Lookup_failed) { return 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"); } } } Framebuffer::Mode mode() override { Area const phys_area(_framebuffer.mode().width(), _framebuffer.mode().height()); Area const session_area = ::Session::screen_area(phys_area); return Framebuffer::Mode(session_area.w(), session_area.h(), _framebuffer.mode().format()); } void mode_sigh(Signal_context_capability sigh) override { _mode_sigh = sigh; } void buffer(Framebuffer::Mode mode, bool use_alpha) override { /* check if the session quota suffices for the specified mode */ if (_session_alloc.quota() < ram_quota(mode, use_alpha)) throw Nitpicker::Session::Out_of_metadata(); _framebuffer_session_component.notify_mode_change(mode, use_alpha); } void focus(Genode::Capability session_cap) override { /* check permission by comparing session labels */ if (!_focus_change_permitted()) { PWRN("unauthorized focus change requesed by %s", label().string()); return; } /* prevent focus changes during drag operations */ if (_mode.drag()) return; /* lookup targeted session object */ Session_component * const session = session_cap.valid() ? dynamic_cast(_ep.lookup_and_lock(session_cap)) : 0; _mode.focused_session(session); if (session) session->release(); report_session(_focus_reporter, session); } void session_control(Label suffix, Session_control control) override { char selector[Label::size()]; Genode::snprintf(selector, sizeof(selector), "%s%s%s", label().string(), suffix.length() ? " -> " : "", suffix.string()); switch (control) { case SESSION_CONTROL_HIDE: _view_stack.visible(selector, false); break; case SESSION_CONTROL_SHOW: _view_stack.visible(selector, true); break; case SESSION_CONTROL_TO_FRONT: _view_stack.to_front(selector); break; } } /******************************* ** Buffer_provider interface ** *******************************/ Buffer *realloc_buffer(Framebuffer::Mode mode, bool use_alpha) { typedef Pixel_rgb565 PT; Area const size(mode.width(), mode.height()); _buffer_size = Chunky_dataspace_texture::calc_num_bytes(size, use_alpha); /* * Preserve the content of the original buffer if nitpicker has * enough lack memory to temporarily keep the original pixels. */ Texture const *src_texture = nullptr; if (::Session::texture()) { enum { PRESERVED_RAM = 128*1024 }; if (env()->ram_session()->avail() > _buffer_size + PRESERVED_RAM) { src_texture = static_cast const *>(::Session::texture()); } else { PWRN("not enough RAM to preserve buffer content during resize"); _release_buffer(); } } Chunky_dataspace_texture * const texture = new (&_session_alloc) Chunky_dataspace_texture(size, use_alpha); /* copy old buffer content into new buffer and release old buffer */ if (src_texture) { Genode::Surface surface(texture->pixel(), texture->Texture_base::size()); Texture_painter::paint(surface, *src_texture, Color(), Point(0, 0), Texture_painter::SOLID, false); _release_buffer(); } if (!_session_alloc.withdraw(_buffer_size)) { destroy(&_session_alloc, texture); _buffer_size = 0; return 0; } ::Session::texture(texture, use_alpha); ::Session::input_mask(texture->input_mask_buffer()); return texture; } }; template class Nitpicker::Root : public Genode::Root_component { private: Session_list &_session_list; Domain_registry const &_domain_registry; Global_keys &_global_keys; Framebuffer::Mode _scr_mode; View_stack &_view_stack; Mode &_mode; ::View &_pointer_origin; Framebuffer::Session &_framebuffer; Genode::Reporter &_focus_reporter; protected: Session_component *_create_session(const char *args) { PINF("create session with args: %s\n", args); size_t const ram_quota = Arg_string::find_arg(args, "ram_quota").ulong_value(0); size_t const required_quota = Input::Session_component::ev_ds_size() + Genode::align_addr(sizeof(Session::Command_buffer), 12); if (ram_quota < required_quota) { PWRN("Insufficient dontated ram_quota (%zd bytes), require %zd bytes", ram_quota, required_quota); throw Root::Quota_exceeded(); } size_t const unused_quota = ram_quota - required_quota; Session_label const label(args); bool const provides_default_bg = (strcmp(label.string(), "backdrop") == 0); Session_component *session = new (md_alloc()) Session_component(Session_label(args), _view_stack, _mode, _pointer_origin, *ep(), _framebuffer, provides_default_bg, *md_alloc(), unused_quota, _focus_reporter); session->apply_session_policy(_domain_registry); _session_list.insert(session); _global_keys.apply_config(_session_list); return session; } void _upgrade_session(Session_component *s, const char *args) { size_t ram_quota = Arg_string::find_arg(args, "ram_quota").ulong_value(0); s->upgrade_ram_quota(ram_quota); } void _destroy_session(Session_component *session) { _session_list.remove(session); _global_keys.apply_config(_session_list); session->destroy_all_views(); _mode.forget(*session); destroy(md_alloc(), session); } public: /** * Constructor */ Root(Session_list &session_list, Domain_registry const &domain_registry, Global_keys &global_keys, Rpc_entrypoint &session_ep, View_stack &view_stack, Mode &mode, ::View &pointer_origin, Allocator &md_alloc, Framebuffer::Session &framebuffer, Genode::Reporter &focus_reporter) : Root_component(&session_ep, &md_alloc), _session_list(session_list), _domain_registry(domain_registry), _global_keys(global_keys), _view_stack(view_stack), _mode(mode), _pointer_origin(pointer_origin), _framebuffer(framebuffer), _focus_reporter(focus_reporter) { } }; struct Nitpicker::Main { Server::Entrypoint &ep; /* * Sessions to the required external services */ Framebuffer::Connection framebuffer; Input::Connection input; Input::Event * const ev_buf = env()->rm_session()->attach(input.dataspace()); typedef Pixel_rgb565 PT; /* physical pixel type */ /* * Initialize framebuffer * * The framebuffer is encapsulated in a volatile object to allow its * reconstruction at runtime as a response to resolution changes. */ struct Framebuffer_screen { Framebuffer::Session &framebuffer; Framebuffer::Mode const mode = framebuffer.mode(); Attached_dataspace fb_ds = { framebuffer.dataspace() }; Screen screen = { fb_ds.local_addr(), Area(mode.width(), mode.height()) }; /** * Constructor */ Framebuffer_screen(Framebuffer::Session &fb) : framebuffer(fb) { } }; Genode::Volatile_object fb_screen = { framebuffer }; void handle_fb_mode(unsigned); Signal_rpc_member
fb_mode_dispatcher = { ep, *this, &Main::handle_fb_mode }; /* * User-input policy */ Global_keys global_keys; Session_list session_list; /* * Construct empty domain registry. The initial version will be replaced * on the first call of 'handle_config'. */ Genode::Volatile_object domain_registry { *env()->heap(), Genode::Xml_node("") }; User_state user_state = { global_keys, fb_screen->screen.size() }; /* * Create view stack with default elements */ Pointer_origin pointer_origin; Background background = { Area(99999, 99999) }; /* * Initialize Nitpicker root interface */ Genode::Sliced_heap sliced_heap = { env()->ram_session(), env()->rm_session() }; Genode::Reporter pointer_reporter = { "pointer" }; Genode::Reporter hover_reporter = { "hover" }; Genode::Reporter focus_reporter = { "focus" }; Genode::Reporter xray_reporter = { "xray" }; Root np_root = { session_list, *domain_registry, global_keys, ep.rpc_ep(), user_state, user_state, pointer_origin, sliced_heap, framebuffer, focus_reporter }; /* * Configuration-update dispatcher, executed in the context of the RPC * entrypoint. * * In addition to installing the signal dispatcher, we trigger first signal * manually to turn the initial configuration into effect. */ void handle_config(unsigned); Signal_rpc_member
config_dispatcher = { ep, *this, &Main::handle_config}; /** * Signal handler invoked on the reception of user input */ void handle_input(unsigned); Signal_rpc_member
input_dispatcher = { ep, *this, &Main::handle_input }; /* * Dispatch input and redraw periodically */ Timer::Connection timer; /** * Perform redraw and flush pixels to the framebuffer */ void draw_and_flush() { user_state.draw(fb_screen->screen).flush([&] (Rect const &rect) { framebuffer.refresh(rect.x1(), rect.y1(), rect.w(), rect.h()); }); } Main(Server::Entrypoint &ep) : ep(ep) { // tmp_fb = &framebuffer; user_state.default_background(background); user_state.stack(pointer_origin); user_state.stack(background); config()->sigh(config_dispatcher); handle_config(0); timer.sigh(input_dispatcher); timer.trigger_periodic(10*1000); framebuffer.mode_sigh(fb_mode_dispatcher); env()->parent()->announce(ep.manage(np_root)); } }; void Nitpicker::Main::handle_input(unsigned) { /* * If kill mode is already active, we got recursively called from * within this 'input_func' (via 'wait_and_dispatch_one_signal'). * In this case, return immediately. New input events will be * processed in the local 'do' loop. */ if (user_state.kill()) return; do { Point const old_pointer_pos = user_state.pointer_pos(); ::Session * const old_pointed_session = user_state.pointed_session(); ::Session * const old_focused_session = user_state.Mode::focused_session(); bool const old_kill_mode = user_state.kill(); bool const old_xray_mode = user_state.xray(); /* handle batch of pending events */ if (input.is_pending()) import_input_events(ev_buf, input.flush(), user_state); Point const new_pointer_pos = user_state.pointer_pos(); ::Session * const new_pointed_session = user_state.pointed_session(); ::Session * const new_focused_session = user_state.Mode::focused_session(); bool const new_kill_mode = user_state.kill(); bool const new_xray_mode = user_state.xray(); /* report mouse-position updates */ if (pointer_reporter.is_enabled() && old_pointer_pos != new_pointer_pos) { Genode::Reporter::Xml_generator xml(pointer_reporter, [&] () { xml.attribute("xpos", new_pointer_pos.x()); xml.attribute("ypos", new_pointer_pos.y()); }); } if (xray_reporter.is_enabled() && old_xray_mode != new_xray_mode) { Genode::Reporter::Xml_generator xml(xray_reporter, [&] () { xml.attribute("enabled", new_xray_mode ? "yes" : "no"); }); } /* report hover changes */ if (old_pointed_session != new_pointed_session) report_session(hover_reporter, new_pointed_session); /* report focus changes */ if (old_focused_session != new_focused_session) report_session(focus_reporter, new_focused_session); /* report kill mode */ if (old_kill_mode != new_kill_mode) { if (new_kill_mode) report_kill_focus(focus_reporter); if (!new_kill_mode) report_session(focus_reporter, new_focused_session); } /* * Continuously redraw the whole screen when kill mode is active. * Otherwise client updates (e.g., the status bar) would stay invisible * because we do not dispatch the RPC interface during kill mode. */ if (new_kill_mode) user_state.update_all_views(); /* update mouse cursor */ if (old_pointer_pos != new_pointer_pos) user_state.geometry(pointer_origin, Rect(new_pointer_pos, Area())); /* perform redraw and flush pixels to the framebuffer */ user_state.draw(fb_screen->screen).flush([&] (Rect const &rect) { framebuffer.refresh(rect.x1(), rect.y1(), rect.w(), rect.h()); }); user_state.mark_all_views_as_clean(); /* deliver framebuffer synchronization events */ if (!user_state.kill()) { for (::Session *s = session_list.first(); s; s = s->next()) s->submit_sync(); } /* * In kill mode, we do not leave the dispatch function in order to * block RPC calls from Nitpicker clients. We block for signals * here to stay responsive to user input and configuration changes. * Nested calls of 'input_func' are prevented by the condition * check for 'user_state.kill()' at the beginning of the handler. */ if (user_state.kill()) Server::wait_and_dispatch_one_signal(); } while (user_state.kill()); } /** * Helper function for 'handle_config' */ static void configure_reporter(Genode::Reporter &reporter) { try { Genode::Xml_node config_xml = Genode::config()->xml_node(); reporter.enabled(config_xml.sub_node("report") .attribute(reporter.name().string()) .has_value("yes")); } catch (...) { reporter.enabled(false); } } void Nitpicker::Main::handle_config(unsigned) { config()->reload(); /* update global keys policy */ global_keys.apply_config(session_list); /* update background color */ try { config()->xml_node().sub_node("background") .attribute("color").value(&background.color); } catch (...) { } configure_reporter(pointer_reporter); configure_reporter(hover_reporter); configure_reporter(focus_reporter); configure_reporter(xray_reporter); /* update domain registry and session policies */ for (::Session *s = session_list.first(); s; s = s->next()) s->reset_domain(); try { domain_registry.construct(*env()->heap(), config()->xml_node()); } catch (...) { } for (::Session *s = session_list.first(); s; s = s->next()) s->apply_session_policy(*domain_registry); user_state.apply_origin_policy(pointer_origin); /* * Domains may have changed their layering, resort the view stack with the * new constrains. */ user_state.sort_views_by_layer(); /* redraw */ user_state.update_all_views(); } void Nitpicker::Main::handle_fb_mode(unsigned) { /* reconstruct framebuffer screen and menu bar */ fb_screen.construct(framebuffer); /* let the view stack use the new size */ user_state.size(Area(fb_screen->mode.width(), fb_screen->mode.height())); /* redraw */ user_state.update_all_views(); /* notify clients about the change screen mode */ for (::Session *s = session_list.first(); s; s = s->next()) { Session_component *sc = dynamic_cast(s); if (sc) sc->notify_mode_change(); } } /************ ** Server ** ************/ namespace Server { char const *name() { return "nitpicker_ep"; } size_t stack_size() { return 4*1024*sizeof(long); } void construct(Entrypoint &ep) { static Nitpicker::Main nitpicker(ep); } }