genode/repos/os/src/server/nitpicker/user_state.h

269 lines
6.8 KiB
C++

/*
* \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 <util/xml_generator.h>
#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_ */