diff --git a/repos/os/src/server/nitpicker/input.h b/repos/os/src/server/nitpicker/input.h index ad5dfbb567..ee024a580b 100644 --- a/repos/os/src/server/nitpicker/input.h +++ b/repos/os/src/server/nitpicker/input.h @@ -62,14 +62,23 @@ static Input::Event merge_motion_events(Input::Event const *ev, unsigned n) } -static void import_input_events(Input::Event *ev_buf, unsigned num_ev, +/** + * Feed input event to the user state + * + * \return true if user has been active. A user is active as long as at + * least one key/button is pressed (during drag operations) + * and when a key/button changes it state. + */ +static bool import_input_events(Input::Event *ev_buf, unsigned num_ev, User_state &user_state) { + bool user_is_active = false; + if (num_ev > 0) { /* - * Take events from input event buffer, merge consecutive motion - * events, and pass result to the user state. - */ + * Take events from input event buffer, merge consecutive motion + * events, and pass result to the user state. + */ for (unsigned src_ev_cnt = 0; src_ev_cnt < num_ev; src_ev_cnt++) { Input::Event *e = &ev_buf[src_ev_cnt]; @@ -84,13 +93,20 @@ static void import_input_events(Input::Event *ev_buf, unsigned num_ev, } /* - * If subsequential relative motion events are merged to - * a zero-motion event, drop it. Otherwise, it would be - * misinterpreted as absolute event pointing to (0, 0). - */ + * If subsequential relative motion events are merged to + * a zero-motion event, drop it. Otherwise, it would be + * misinterpreted as absolute event pointing to (0, 0). + */ if (e->is_relative_motion() && curr.rx() == 0 && curr.ry() == 0) continue; + /* + * If we detect a pressed key sometime during the event processing, + * we regard the user as active. This check captures the presence + * of press-release combinations within one batch of input events. + */ + user_is_active |= user_state.key_is_pressed(); + /* pass event to user state */ user_state.handle_event(curr); } @@ -102,6 +118,13 @@ static void import_input_events(Input::Event *ev_buf, unsigned num_ev, */ user_state.handle_event(Input::Event()); } + + /* + * If at least one key is kept pressed, we regard the user as active. + */ + user_is_active |= user_state.key_is_pressed(); + + return user_is_active; } #endif /* _INPUT_H_ */ diff --git a/repos/os/src/server/nitpicker/main.cc b/repos/os/src/server/nitpicker/main.cc index dd246a24d8..8a64cfc33f 100644 --- a/repos/os/src/server/nitpicker/main.cc +++ b/repos/os/src/server/nitpicker/main.cc @@ -76,7 +76,8 @@ Framebuffer::Session *tmp_fb; ** Utilities ** ***************/ -static void report_session(Genode::Reporter &reporter, Session *session) +static void report_session(Genode::Reporter &reporter, Session *session, + bool active = false) { if (!reporter.is_enabled()) return; @@ -92,6 +93,8 @@ static void report_session(Genode::Reporter &reporter, Session *session) Genode::snprintf(buf, sizeof(buf), "#%02x%02x%02x", color.r, color.g, color.b); xml.attribute("color", buf); + + if (active) xml.attribute("active", "yes"); } }); } @@ -1194,6 +1197,30 @@ struct Nitpicker::Main */ Timer::Connection timer; + /** + * Counter that is incremented periodically + */ + unsigned period_cnt = 0; + + /** + * Period counter when the user was active the last time + */ + unsigned last_active_period = 0; + + /** + * Number of periods after the last user activity when we regard the user + * as becoming inactive + */ + unsigned activity_threshold = 50; + + /** + * True if the user was recently active + * + * This state is reported as part of focus reports to allow the clipboard + * to dynamically adjust its information-flow policy to the user activity. + */ + bool user_active = false; + /** * Perform redraw and flush pixels to the framebuffer */ @@ -1225,6 +1252,8 @@ struct Nitpicker::Main void Nitpicker::Main::handle_input(unsigned) { + period_cnt++; + /* * If kill mode is already active, we got recursively called from * within this 'input_func' (via 'wait_and_dispatch_one_signal'). @@ -1240,9 +1269,13 @@ void Nitpicker::Main::handle_input(unsigned) ::Session * const old_focused_session = user_state.Mode::focused_session(); bool const old_kill_mode = user_state.kill(); bool const old_xray_mode = user_state.xray(); + bool const old_user_active = user_active; /* handle batch of pending events */ - import_input_events(ev_buf, input.flush(), user_state); + if (import_input_events(ev_buf, input.flush(), user_state)) { + last_active_period = period_cnt; + user_active = true; + } Point const new_pointer_pos = user_state.pointer_pos(); ::Session * const new_pointed_session = user_state.pointed_session(); @@ -1250,6 +1283,10 @@ void Nitpicker::Main::handle_input(unsigned) bool const new_kill_mode = user_state.kill(); bool const new_xray_mode = user_state.xray(); + /* flag user as inactive after activity threshold is reached */ + if (period_cnt == last_active_period + activity_threshold) + user_active = false; + /* report mouse-position updates */ if (pointer_reporter.is_enabled() && old_pointer_pos != new_pointer_pos) { @@ -1273,8 +1310,9 @@ void Nitpicker::Main::handle_input(unsigned) report_session(hover_reporter, new_pointed_session); /* report focus changes */ - if (old_focused_session != new_focused_session) - report_session(focus_reporter, new_focused_session); + if (old_focused_session != new_focused_session + || old_user_active != user_active) + report_session(focus_reporter, new_focused_session, user_active); /* report kill mode */ if (old_kill_mode != new_kill_mode) { diff --git a/repos/os/src/server/nitpicker/mode.h b/repos/os/src/server/nitpicker/mode.h index cc73a62673..6ad38893e7 100644 --- a/repos/os/src/server/nitpicker/mode.h +++ b/repos/os/src/server/nitpicker/mode.h @@ -53,6 +53,8 @@ class Mode bool has_key_cnt(unsigned cnt) const { return cnt == _key_cnt; } + bool key_is_pressed() const { return _key_cnt > 0; } + Session *focused_session() { return _focused_session; } Session const *focused_session() const { return _focused_session; }