wm/layouter: pointer grabbing/ungrabbing

This patch implements the following policy for applications requesting
exclusive input (relative motion): The pointer is grabbed as soon as the
user clicks inside the application window. It is forcibly ungrabbed on
any window-focus change or when tapping the KEY_SCREEN. An application
can always enable (transient) exclusive input during a key sequence,
e.g., when dragging the mouse while holding the mouse button. Transient
exclusive input is revoked when releasing the last button/key.

Fixes #5355
This commit is contained in:
Norman Feske 2024-10-06 15:13:54 +02:00 committed by Christian Helmuth
parent 92227df624
commit ca47280ce9
8 changed files with 85 additions and 3 deletions

View File

@ -15,7 +15,7 @@
<assign label_prefix="" target="screen_1" xpos="any" ypos="any"/>
</rules>
<press key="KEY_SCREEN">
<press key="KEY_SCREEN" action="release_grab">
<press key="KEY_TAB" action="next_window">
<release key="KEY_TAB">
<release key="KEY_SCREEN" action="raise_window"/>

View File

@ -63,6 +63,8 @@ install_config {
<policy label_prefix="pointer" domain="pointer"/>
<default-policy domain="default"/>
<global-key name="KEY_SCREEN" label="wm -> wm -> decorator" />
</config>
</start>
@ -154,7 +156,9 @@ set fd [open [run_dir]/genode/focus w]
puts $fd "<focus label=\"wm -> focus\"/>"
close $fd
copy_file [genode_dir]/repos/gems/recipes/raw/motif_wm/wm.config [run_dir]/genode/
copy_file [genode_dir]/repos/gems/recipes/raw/motif_wm/wm.config [run_dir]/genode/
copy_file [genode_dir]/repos/gems/recipes/raw/motif_wm/layouter.config [run_dir]/genode/
copy_file [genode_dir]/repos/gems/recipes/raw/motif_wm/decorator_init.config [run_dir]/genode/
build { app/window_layouter app/decorator server/nitpicker server/wm test/nitpicker }

View File

@ -49,6 +49,7 @@ class Window_layouter::Action
PREV_TAB,
TOOGLE_OVERLAY,
SCREEN,
RELEASE_GRAB,
};
private:
@ -64,6 +65,7 @@ class Window_layouter::Action
if (string == "raise_window") return RAISE_WINDOW;
if (string == "toggle_fullscreen") return TOGGLE_FULLSCREEN;
if (string == "screen") return SCREEN;
if (string == "release_grab") return RELEASE_GRAB;
Genode::warning("cannot convert \"", string, "\" to action type");
return NONE;

View File

@ -196,6 +196,12 @@ struct Window_layouter::Main : Operations,
_gen_focus();
}
void release_grab() override
{
/* wm revokes exclusive input on each focus update */
_gen_focus();
}
void toggle_fullscreen(Window_id id) override
{
/* make sure that the specified window is the front-most one */

View File

@ -28,6 +28,7 @@ struct Window_layouter::Operations : Interface
virtual void close(Window_id) = 0;
virtual void toggle_fullscreen(Window_id) = 0;
virtual void focus(Window_id) = 0;
virtual void release_grab() = 0;
virtual void to_front(Window_id) = 0;
virtual void drag(Window_id, Window::Element, Point clicked, Point curr) = 0;
virtual void finalize_drag(Window_id, Window::Element, Point clicked, Point final) = 0;

View File

@ -332,6 +332,10 @@ void Window_layouter::User_state::_handle_event(Input::Event const &e,
_operations.screen(action.target_name());
return;
case Action::RELEASE_GRAB:
_operations.release_grab();
return;
default:
warning("action ", (int)action.type(), " unhanded");
}

View File

@ -560,10 +560,35 @@ class Wm::Gui::Session_component : public Session_object<Gui::Session>,
Input::Session_component _input_session {
_env.ep(), _ram, _env.rm(), *this };
bool _exclusive_input_requested = false,
_exclusive_input_granted = false;
/* used for hiding the click-to-grab event from the client */
bool _consume_one_btn_left_release = false;
/**
* Input::Session_component::Action interface
*/
void exclusive_input_requested(bool) override { }
void exclusive_input_requested(bool const requested) override
{
if (requested == _exclusive_input_requested)
return;
/*
* Allow immediate changes when
*
* 1. Exclusive input is already granted by the user having clicked
* into the window, or
* 2. The client yields the exclusivity, or
* 3. Transient exclusive input is requested while a button is held.
* In this case, exclusive input will be revoked as soon as the
* last button/key is released.
*/
if (_exclusive_input_granted || _key_cnt || !requested)
_gui_input.exclusive(requested);
_exclusive_input_requested = requested;
}
Click_handler &_click_handler;
@ -734,6 +759,31 @@ class Wm::Gui::Session_component : public Session_object<Gui::Session>,
if (propagate_to_pointer_state)
_pointer_state.apply_event(ev);
/*
* Handle pointer grabbing/ungrabbing
*/
/* revoke transient exclusive input (while clicked) */
if (ev.release() && _key_cnt == 0)
if (_exclusive_input_requested && !_exclusive_input_granted)
_gui_input.exclusive(false);
/* grant exclusive input when clicking into window */
if (ev.key_press(Input::BTN_LEFT) && _key_cnt == 1) {
if (_exclusive_input_requested && !_exclusive_input_granted) {
_gui_input.exclusive(true);
_exclusive_input_granted = true;
_consume_one_btn_left_release = true;
continue;
}
}
if (ev.key_release(Input::BTN_LEFT)) {
if (_consume_one_btn_left_release) {
_consume_one_btn_left_release = false;
continue;
}
}
/* submit event to the client */
_input_session.submit(_translate_event(ev, input_origin));
}
@ -1014,6 +1064,14 @@ class Wm::Gui::Session_component : public Session_object<Gui::Session>,
_info_rom->trigger_update();
}
void revoke_exclusive_input()
{
if (_exclusive_input_granted) {
_gui_input.exclusive(false);
_exclusive_input_granted = false;
}
}
/***************************
** GUI session interface **
@ -1650,6 +1708,12 @@ class Wm::Gui::Root : public Rpc_object<Typed_root<Gui::Session> >,
for (Session_component *s = _sessions.first(); s; s = s->next())
s->propagate_mode_change();
}
void revoke_exclusive_input()
{
for (Session_component *s = _sessions.first(); s; s = s->next())
s->revoke_exclusive_input();
}
};
#endif /* _GUI_H_ */

View File

@ -77,6 +77,7 @@ struct Wm::Main : Pointer::Tracker, Gui::Session_component::Action
void _handle_focus_update()
{
_gui_root.revoke_exclusive_input();
_focus_rom.update();
_focus_rom.xml().with_optional_sub_node("window", [&] (Xml_node const &window) {
_with_win_id_from_xml(window, [&] (Window_registry::Id id) {