diff --git a/repos/os/src/server/nitpicker/README b/repos/os/src/server/nitpicker/README index a55beff491..3833c0197b 100644 --- a/repos/os/src/server/nitpicker/README +++ b/repos/os/src/server/nitpicker/README @@ -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 '' 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 --------------- diff --git a/repos/os/src/server/nitpicker/focus.h b/repos/os/src/server/nitpicker/focus.h index d0e2a7ee96..38d3f253b8 100644 --- a/repos/os/src/server/nitpicker/focus.h +++ b/repos/os/src/server/nitpicker/focus.h @@ -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) diff --git a/repos/os/src/server/nitpicker/main.cc b/repos/os/src/server/nitpicker/main.cc index dc4b93eac6..6b24c9b5f7 100644 --- a/repos/os/src/server/nitpicker/main.cc +++ b/repos/os/src/server/nitpicker/main.cc @@ -32,6 +32,9 @@ #include "domain_registry.h" namespace Nitpicker { + + struct Focus_updater { virtual void update_focus() = 0; }; + template class Root; struct Main; } @@ -83,6 +86,7 @@ class Nitpicker::Root : public Root_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->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, 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(&env.ep().rpc_ep(), &md_alloc), _env(env), _config(config), _session_list(session_list), @@ -153,7 +159,7 @@ class Nitpicker::Root : public Root_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, }; -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 _root = { _env, _config, _session_list, *_domain_registry, + Constructible _focus_rom; + + Root _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
_config_handler = { _env.ep(), *this, &Main::_handle_config}; + Signal_handler
_config_handler = { _env.ep(), *this, &Main::_handle_config }; + + /** + * Signal handler for externally triggered focus changes + */ + void _handle_focus(); + + Signal_handler
_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(); diff --git a/repos/os/src/server/nitpicker/user_state.cc b/repos/os/src/server/nitpicker/user_state.cc index fc6aeffb3d..0d942073a8 100644 --- a/repos/os/src/server/nitpicker/user_state.cc +++ b/repos/os/src/server/nitpicker/user_state.cc @@ -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; diff --git a/repos/os/src/server/nitpicker/user_state.h b/repos/os/src/server/nitpicker/user_state.h index 9fa0e24422..becc1a1fbd 100644 --- a/repos/os/src/server/nitpicker/user_state.h +++ b/repos/os/src/server/nitpicker/user_state.h @@ -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_ */