diff --git a/repos/gems/recipes/raw/motif_wm/layouter.config b/repos/gems/recipes/raw/motif_wm/layouter.config
index 4611a48a59..1560e09e17 100644
--- a/repos/gems/recipes/raw/motif_wm/layouter.config
+++ b/repos/gems/recipes/raw/motif_wm/layouter.config
@@ -15,7 +15,7 @@
-
+
diff --git a/repos/gems/run/wm.run b/repos/gems/run/wm.run
index 3cf51763a3..4b05218668 100644
--- a/repos/gems/run/wm.run
+++ b/repos/gems/run/wm.run
@@ -63,6 +63,8 @@ install_config {
+
+
@@ -154,7 +156,9 @@ set fd [open [run_dir]/genode/focus w]
puts $fd " 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 }
diff --git a/repos/gems/src/app/window_layouter/action.h b/repos/gems/src/app/window_layouter/action.h
index 45890aa426..995274b6ac 100644
--- a/repos/gems/src/app/window_layouter/action.h
+++ b/repos/gems/src/app/window_layouter/action.h
@@ -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;
diff --git a/repos/gems/src/app/window_layouter/main.cc b/repos/gems/src/app/window_layouter/main.cc
index f4a54e413a..123428445f 100644
--- a/repos/gems/src/app/window_layouter/main.cc
+++ b/repos/gems/src/app/window_layouter/main.cc
@@ -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 */
diff --git a/repos/gems/src/app/window_layouter/operations.h b/repos/gems/src/app/window_layouter/operations.h
index 4ed9922d13..5cf7da46cc 100644
--- a/repos/gems/src/app/window_layouter/operations.h
+++ b/repos/gems/src/app/window_layouter/operations.h
@@ -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;
diff --git a/repos/gems/src/app/window_layouter/user_state.h b/repos/gems/src/app/window_layouter/user_state.h
index 255b17dca8..edb2d8cf8e 100644
--- a/repos/gems/src/app/window_layouter/user_state.h
+++ b/repos/gems/src/app/window_layouter/user_state.h
@@ -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");
}
diff --git a/repos/gems/src/server/wm/gui.h b/repos/gems/src/server/wm/gui.h
index bd9b53e46a..a289824a30 100644
--- a/repos/gems/src/server/wm/gui.h
+++ b/repos/gems/src/server/wm/gui.h
@@ -560,10 +560,35 @@ class Wm::Gui::Session_component : public Session_object,
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,
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,
_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 >,
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_ */
diff --git a/repos/gems/src/server/wm/main.cc b/repos/gems/src/server/wm/main.cc
index 41feacea3a..02cde0a789 100644
--- a/repos/gems/src/server/wm/main.cc
+++ b/repos/gems/src/server/wm/main.cc
@@ -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) {