/* * \brief Nitpicker user state handling * \author Norman Feske * \date 2006-08-09 * * This class comprehends the policy of user interaction. * It manages the toggling of Nitpicker's different modes * and routes input events to corresponding client sessions. */ /* * Copyright (C) 2006-2017 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ #ifndef _USER_STATE_H_ #define _USER_STATE_H_ #include #include "focus.h" #include "view_stack.h" #include "global_keys.h" namespace Nitpicker { class User_state; } class Nitpicker::User_state { private: /* * Noncopyable */ User_state(User_state const &); User_state &operator = (User_state const &); /* * Number of currently pressed keys. This counter is used to determine * if the user is dragging an item. */ unsigned _key_cnt = 0; View_owner *_focused = nullptr; View_owner *_next_focused = nullptr; /* * True while a global key sequence is processed */ 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 */ Focus &_focus; /** * Policy for the routing of global keys */ Global_keys &_global_keys; /** * View stack, used to determine the hovered view and pointer boundary */ View_stack &_view_stack; /* * Current pointer position */ Point _pointer_pos; /* * Currently pointed-at view owner */ View_owner *_hovered = nullptr; /* * View owner that receives the current stream of input events */ View_owner *_input_receiver = nullptr; /** * View owner that was last clicked-on by the user */ View_owner *_last_clicked = nullptr; /** * Number of clicks, used to detect whether a focus-relevant click * happened during '_handle_input_event'. */ unsigned _clicked_count = 0; /** * Version supplement for the "clicked" report * * The value allows the receiver of the report to detect the situation * where two consecutive clicks refer to the same client but both * events require a distinct focus response, i.e., if the focus (focus * ROM) was changed in-between both clicks by other means than a click. */ unsigned _last_clicked_version = 0; /* * If set, a "clicked" report is generated even if the clicked-on view * is the same as the previously clicked-on view. */ bool _last_clicked_redeliver = false; /** * Array for tracking the state of each key */ struct Key_array { struct State { bool pressed = false; }; State _states[Input::KEY_MAX + 1]; void pressed(Input::Keycode key, bool pressed) { if (key <= Input::KEY_MAX) _states[key].pressed = pressed; } bool pressed(Input::Keycode key) const { return (key <= Input::KEY_MAX) && _states[key].pressed; } void report_state(Genode::Xml_generator &xml) const { for (unsigned i = 0; i <= Input::KEY_MAX; i++) if (_states[i].pressed) xml.node("pressed", [&] () { xml.attribute("key", Input::key_name((Input::Keycode)i)); }); } } _key_array { }; void _focus_view_owner_via_click(View_owner &); void _handle_input_event(Input::Event); bool _key_pressed() const { return _key_cnt > 0; } /** * Apply pending focus-change request that was issued during drag state */ void _apply_pending_focus_change() { /* * Defer focus changes to a point where no drag operation is in * flight because otherwise, the involved sessions would obtain * inconsistent press and release events. However, focus changes * during global key sequences are fine. */ if (_key_pressed() && !_global_key_sequence) return; if (_focused != _next_focused) { _focused = _next_focused; /* * Enforce the generation of a new "clicked" report for any click * that follows a focus change. This is needed in situations * where the focus is defined by clicks as well as other means * (e.g., the appearance of a lock screen). */ _last_clicked_redeliver = true; /* propagate changed focus to view stack */ if (_focused) _focus.assign(*_focused); else _focus.reset(); } } public: /** * Constructor * * \param focus exported focus information, to be consumed by the * view stack to tailor its view drawing operations */ User_state(Focus &focus, Global_keys &global_keys, View_stack &view_stack, Point initial_pointer_pos) : _focus(focus), _global_keys(global_keys), _view_stack(view_stack), _pointer_pos(initial_pointer_pos) { } /**************************************** ** Interface used by the main program ** ****************************************/ struct Handle_input_result { bool const hover_changed; bool const focus_changed; bool const key_state_affected; bool const button_activity; bool const motion_activity; bool const key_pressed; bool const last_clicked_changed; }; Handle_input_result handle_input_events(Input::Event const *ev_buf, unsigned num_ev); /** * Discard all references to specified view owner */ struct Handle_forget_result { bool const hover_changed; bool const focus_changed; }; Handle_forget_result forget(View_owner const &); void report_keystate(Xml_generator &) const; void report_pointer_position(Xml_generator &) const; void report_hovered_view_owner(Xml_generator &, bool motion_active) const; void report_focused_view_owner(Xml_generator &, bool button_active) const; 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_ */