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

263 lines
6.4 KiB
C++

/*
* \brief User state implementation
* \author Norman Feske
* \date 2006-08-27
*/
/*
* 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.
*/
#include <input/event.h>
#include <input/keycodes.h>
#include "user_state.h"
using namespace Input;
/***************
** Utilities **
***************/
static inline bool _mouse_button(Keycode keycode) {
return keycode >= BTN_LEFT && keycode <= BTN_MIDDLE; }
/**************************
** User state interface **
**************************/
User_state::User_state(Global_keys &global_keys, Area view_stack_size)
:
View_stack(view_stack_size, *this), _global_keys(global_keys)
{ }
void User_state::_update_all()
{
update_all_views();
}
void User_state::handle_event(Input::Event ev)
{
Input::Keycode const keycode = ev.keycode();
Input::Event::Type const type = ev.type();
/*
* Mangle incoming events
*/
int ax = _pointer_pos.x(), ay = _pointer_pos.y();
int rx = 0, ry = 0; /* skip info about relative motion by default */
/* transparently handle absolute and relative motion events */
if (type == Event::MOTION) {
if ((ev.rx() || ev.ry()) && ev.ax() == 0 && ev.ay() == 0) {
ax = Genode::max(0, Genode::min((int)size().w() - 1, ax + ev.rx()));
ay = Genode::max(0, Genode::min((int)size().h() - 1, ay + ev.ry()));
} else {
ax = ev.ax();
ay = ev.ay();
}
}
/* propagate relative motion for wheel events */
if (type == Event::WHEEL) {
rx = ev.rx();
ry = ev.ry();
}
if (type == Event::TOUCH) {
ax = ev.ax();
ay = ev.ay();
ev = Input::Event::create_touch_event(ax, ay, ev.code(),
ev.is_touch_release());
} else
ev = Input::Event(type, keycode, ax, ay, rx, ry);
_pointer_pos = Point(ax, ay);
/* count keys */
if (type == Event::PRESS) Mode::inc_key_cnt();
if (type == Event::RELEASE && Mode::drag()) Mode::dec_key_cnt();
View const * const pointed_view = find_view(_pointer_pos);
::Session * const pointed_session = pointed_view ? &pointed_view->session() : 0;
/*
* Deliver a leave event if pointed-to session changed
*/
if (_pointed_session && (pointed_session != _pointed_session)) {
Input::Event leave_ev(Input::Event::LEAVE, 0, ax, ay, 0, 0);
_pointed_session->submit_input_event(leave_ev);
}
_pointed_session = pointed_session;
/**
* Guard that, when 'enabled' is set to true, performs a whole-screen
* update when leaving the scope
*/
struct Update_all_guard
{
User_state &user_state;
bool update = false;
Update_all_guard(User_state &user_state)
: user_state(user_state) { }
~Update_all_guard()
{
if (update)
user_state._update_all();
}
} update_all_guard(*this);
/*
* Handle start of a key sequence
*/
if (type == Event::PRESS && Mode::has_key_cnt(1)) {
::Session *global_receiver = nullptr;
/* update focused session */
if (_mouse_button(keycode)
&& _pointed_session
&& (_pointed_session != Mode::focused_session())
&& (_pointed_session->has_focusable_domain()
|| _pointed_session->has_same_domain(Mode::focused_session()))) {
update_all_guard.update = true;
/*
* Notify both the old focused session and the new one.
*/
if (Mode::focused_session()) {
Input::Event unfocus_ev(Input::Event::FOCUS, 0, ax, ay, 0, 0);
Mode::focused_session()->submit_input_event(unfocus_ev);
}
if (_pointed_session) {
Input::Event focus_ev(Input::Event::FOCUS, 1, ax, ay, 0, 0);
_pointed_session->submit_input_event(focus_ev);
}
if (_pointed_session->has_transient_focusable_domain())
global_receiver = _pointed_session;
else
focused_session(_pointed_session);
}
/*
* If there exists a global rule for the pressed key, set the
* corresponding session as receiver of the input stream until the key
* count reaches zero. Otherwise, the input stream is directed to the
* pointed-at session.
*
* If we deliver a global key sequence, we temporarily change the focus
* to the global receiver. To reflect that change, we need to update
* the whole screen.
*/
if (!global_receiver)
global_receiver = _global_keys.global_receiver(keycode);
if (global_receiver) {
_global_key_sequence = true;
_input_receiver = global_receiver;
update_all_guard.update = true;
}
/*
* No global rule matched, so the input stream gets directed to the
* focused session or refers to a built-in operation.
*/
if (!global_receiver) {
_input_receiver = Mode::focused_session();
}
}
/*
* Deliver event to session
*/
if (type == Event::MOTION || type == Event::WHEEL || type == Event::TOUCH) {
if (Mode::has_key_cnt(0)) {
if (_pointed_session) {
/*
* Unless the domain of the pointed session is configured to
* always receive hover events, we deliver motion events only
* to the focused domain.
*/
if (_pointed_session->hover_always()
|| _pointed_session->has_same_domain(Mode::focused_session()))
_pointed_session->submit_input_event(ev);
}
} else if (_input_receiver)
_input_receiver->submit_input_event(ev);
}
/*
* Deliver press/release event to focused session or the receiver of global
* key.
*/
if ((type == Event::PRESS) && _input_receiver) {
if (!_mouse_button(ev.keycode())
|| (_pointed_session
&& (_pointed_session->has_focusable_domain()
|| _pointed_session->has_same_domain(Mode::focused_session()))))
_input_receiver->submit_input_event(ev);
else
_input_receiver = nullptr;
}
if ((type == Event::RELEASE) && _input_receiver)
_input_receiver->submit_input_event(ev);
/*
* Detect end of global key sequence
*/
if (ev.type() == Event::RELEASE && Mode::has_key_cnt(0) && _global_key_sequence) {
_input_receiver = Mode::focused_session();
update_all_guard.update = true;
_global_key_sequence = false;
}
}
/********************
** Mode interface **
********************/
void User_state::forget(::Session const &session)
{
Mode::forget(session);
if (_pointed_session == &session) {
View * const pointed_view = find_view(_pointer_pos);
_pointed_session = pointed_view ? &pointed_view->session() : nullptr;
}
if (_input_receiver == &session)
_input_receiver = nullptr;
}
void User_state::focused_session(::Session *session)
{
Mode::focused_session(session);
if (!_global_key_sequence)
_input_receiver = session;
}