nitpicker: respond to external focus policy

This patch enables nitpicker to use an external focus policy instead of
the traditional builtin click-to-focus policy. The external focus policy
is obtained from a 'focus' ROM. The focus ROM is expected to have a
'label' attribute with the value set to the label of the to-be focused
client.
This commit is contained in:
Norman Feske 2017-11-19 22:24:58 +01:00 committed by Christian Helmuth
parent 7ca56a62fd
commit 544274feb9
5 changed files with 144 additions and 15 deletions

View File

@ -122,6 +122,20 @@ If not specified, the focus attribute defaults to 'none'.
Note that the input focus may also be manipulated via nitpicker's session
interface, which allows a client to yield the focus to other clients.
By default, nitpicker has a builtin click-to-focus policy where focus changes
are initiated exclusively by the user by clicking on a view of an unfocused
client. However, the builtin policy is not always desired. For example, in
console-like scenarios, the focus should always correspond to the visible
console regardless of any mouse clicks. Nitpicker supports such scenarios by
the use of an external focus-policy component (e.g., nit_focus). By setting
the '<config>' attribute 'focus' to "rom", the focus is obtained from an
external ROM module named "focus". The focus ROM is expected to have a 'label'
attribute with the value set to the label of the to-be focused client. When
using an external focus policy, domains configured as "click" are internally
handled like "transient" focusable domains. It is up to the external focus
policy component to make a new focus permanent by providing an updated "focus"
ROM.
Hovering policy
---------------

View File

@ -65,6 +65,8 @@ class Nitpicker::Focus : Noncopyable
*/
void assign(View_owner const &focused) { _focused = &focused; }
void reset() { _focused = nullptr; }
void forget(View_owner const &owner)
{
if (_focused == &owner)

View File

@ -32,6 +32,9 @@
#include "domain_registry.h"
namespace Nitpicker {
struct Focus_updater { virtual void update_focus() = 0; };
template <typename> class Root;
struct Main;
}
@ -83,6 +86,7 @@ class Nitpicker::Root : public Root_component<Session_component>,
View_component &_builtin_background;
Framebuffer::Session &_framebuffer;
Reporter &_focus_reporter;
Focus_updater &_focus_updater;
protected:
@ -113,6 +117,7 @@ class Nitpicker::Root : public Root_component<Session_component>,
session->apply_session_policy(_config.xml(), _domain_registry);
_session_list.insert(session);
_global_keys.apply_config(_config.xml(), _session_list);
_focus_updater.update_focus();
return session;
}
@ -144,7 +149,8 @@ class Nitpicker::Root : public Root_component<Session_component>,
Global_keys &global_keys, View_stack &view_stack,
User_state &user_state, View_component &pointer_origin,
View_component &builtin_background, Allocator &md_alloc,
Framebuffer::Session &framebuffer, Reporter &focus_reporter)
Framebuffer::Session &framebuffer, Reporter &focus_reporter,
Focus_updater &focus_updater)
:
Root_component<Session_component>(&env.ep().rpc_ep(), &md_alloc),
_env(env), _config(config), _session_list(session_list),
@ -153,7 +159,7 @@ class Nitpicker::Root : public Root_component<Session_component>,
_pointer_origin(pointer_origin),
_builtin_background(builtin_background),
_framebuffer(framebuffer),
_focus_reporter(focus_reporter)
_focus_reporter(focus_reporter), _focus_updater(focus_updater)
{ }
@ -185,7 +191,7 @@ class Nitpicker::Root : public Root_component<Session_component>,
};
struct Nitpicker::Main
struct Nitpicker::Main : Focus_updater
{
Env &_env;
@ -268,12 +274,21 @@ struct Nitpicker::Main
Reporter _keystate_reporter = { _env, "keystate" };
Reporter _clicked_reporter = { _env, "clicked" };
Attached_rom_dataspace _config { _env, "config" };
Attached_rom_dataspace _config_rom { _env, "config" };
Root<PT> _root = { _env, _config, _session_list, *_domain_registry,
Constructible<Attached_rom_dataspace> _focus_rom;
Root<PT> _root = { _env, _config_rom, _session_list, *_domain_registry,
_global_keys, _view_stack, _user_state, _pointer_origin,
_builtin_background, _sliced_heap, _framebuffer,
_focus_reporter };
_focus_reporter, *this };
/**
* Focus_updater interface
*
* Called whenever a new session appears.
*/
void update_focus() override { _handle_focus(); }
/*
* Configuration-update handler, executed in the context of the RPC
@ -284,7 +299,14 @@ struct Nitpicker::Main
*/
void _handle_config();
Signal_handler<Main> _config_handler = { _env.ep(), *this, &Main::_handle_config};
Signal_handler<Main> _config_handler = { _env.ep(), *this, &Main::_handle_config };
/**
* Signal handler for externally triggered focus changes
*/
void _handle_focus();
Signal_handler<Main> _focus_handler = { _env.ep(), *this, &Main::_handle_focus };
/**
* Signal handler invoked on the reception of user input
@ -333,7 +355,7 @@ struct Nitpicker::Main
_view_stack.stack(_pointer_origin);
_view_stack.stack(_builtin_background);
_config.sigh(_config_handler);
_config_rom.sigh(_config_handler);
_handle_config();
_framebuffer.sync_sigh(_input_handler);
@ -433,11 +455,34 @@ static void configure_reporter(Genode::Xml_node config, Genode::Reporter &report
}
void Nitpicker::Main::_handle_focus()
{
if (!_focus_rom.constructed())
return;
_focus_rom->update();
typedef Session::Label Label;
Label const label = _focus_rom->xml().attribute_value("label", Label());
/* determine session that matches the label found in the focus ROM */
View_owner *next_focus = nullptr;
for (Session_component *s = _session_list.first(); s; s = s->next())
if (s->label() == label)
next_focus = s;
if (next_focus)
_user_state.focus(*next_focus);
else
_user_state.reset_focus();
}
void Nitpicker::Main::_handle_config()
{
_config.update();
_config_rom.update();
Xml_node const config = _config.xml();
Xml_node const config = _config_rom.xml();
/* update global keys policy */
_global_keys.apply_config(config, _session_list);
@ -473,6 +518,22 @@ void Nitpicker::Main::_handle_config()
*/
_view_stack.sort_views_by_layer();
/*
* Respond to a configuration change of the input-focus mechanism
*/
bool const focus_rom = (config.attribute_value("focus", String<16>()) == "rom");
if (_focus_rom.constructed() && !focus_rom)
_focus_rom.destruct();
if (!_focus_rom.constructed() && focus_rom) {
_focus_rom.construct(_env, "focus");
_focus_rom->sigh(_focus_handler);
_handle_focus();
}
/* disable builtin focus handling when using an external focus policy */
_user_state.focus_via_click(!_focus_rom.constructed());
/* redraw */
_view_stack.update_all_views();

View File

@ -176,7 +176,21 @@ void User_state::_handle_input_event(Input::Event ev)
if (_hovered->has_transient_focusable_domain()) {
global_receiver = _hovered;
} else {
_focus_view_owner_via_click(*_hovered);
/*
* Distinguish the use of the builtin focus switching and the
* alternative use of an external focus policy. In the latter
* case, focusable domains are handled like transiently
* focusable domains. The permanent focus change is triggered
* by an external focus-policy component by posting and updated
* focus ROM, which is then propagated into the user state via
* the 'User_state::focus' and 'User_state::reset_focus'
* methods.
*/
if (_focus_via_click)
_focus_view_owner_via_click(*_hovered);
else
global_receiver = _hovered;
_last_clicked = _hovered;
}
}
@ -434,9 +448,6 @@ bool User_state::_focus_change_permitted(View_owner const &caller) const
void User_state::_focus_view_owner_via_click(View_owner &owner)
{
_focus.assign(owner);
_focused = &owner;
_next_focused = &owner;
_focused = &owner;

View File

@ -46,6 +46,14 @@ class Nitpicker::User_state : public Focus_controller
*/
bool _global_key_sequence = false;
/*
* True if the input focus should change directly whenever the user
* clicks on an unfocused client. This is the traditional behaviour
* of nitpicker. This builtin policy is now superseded by the use of an
* external focus-management component (e.g., nit_focus).
*/
bool _focus_via_click = true;
/**
* Input-focus information propagated to the view stack
*/
@ -133,8 +141,15 @@ class Nitpicker::User_state : public Focus_controller
if (_key_pressed() && !_global_key_sequence)
return;
if (_focused != _next_focused)
if (_focused != _next_focused) {
_focused = _next_focused;
/* propagate changed focus to view stack */
if (_focused)
_focus.assign(*_focused);
else
_focus.reset();
}
}
public:
@ -205,6 +220,32 @@ class Nitpicker::User_state : public Focus_controller
void report_last_clicked_view_owner(Xml_generator &) const;
Point pointer_pos() { return _pointer_pos; }
/**
* Enable/disable direct focus changes by clicking on a client
*/
void focus_via_click(bool enabled) { _focus_via_click = enabled; }
/**
* Set input focus to specified view owner
*
* This method is used when nitpicker's focus is managed by an
* external focus-policy component like 'nit_focus'.
*
* The focus change is not applied immediately but deferred to the
* next call of 'handle_input_events' (which happens periodically).
*/
void focus(View_owner &owner)
{
/*
* The focus change is not applied immediately but deferred to the
* next call of '_apply_pending_focus_change' via the periodic
* call of 'handle_input_events'.
*/
_next_focused = &owner;
}
void reset_focus() { _next_focused = nullptr; }
};
#endif /* _USER_STATE_H_ */