WindowServer: Always send mouse events to active input tracker first

If a window is currently actively tracking input events (because
sent it a MouseDown and haven't sent it a MouseUp yet), we now simply
send mouse events to that window right away before doing any other
event processing.

This makes it much easier to reason about mouse events.
This commit is contained in:
Andreas Kling 2021-06-18 12:39:20 +02:00
parent 69dad3b996
commit 82a945fa7e
Notes: sideshowbarker 2024-07-18 12:04:49 +09:00
2 changed files with 85 additions and 80 deletions

View file

@ -919,17 +919,39 @@ void WindowManager::deliver_mouse_event(Window& window, MouseEvent& event, bool
}
}
bool WindowManager::process_ongoing_active_input_mouse_event(MouseEvent& event, Window*& hovered_window)
{
if (!m_active_input_tracking_window)
return false;
// At this point, we have delivered the start of an input sequence to a
// client application. We must keep delivering to that client
// application until the input sequence is done.
//
// This prevents e.g. moving on one window out of the bounds starting
// a move in that other unrelated window, and other silly shenanigans.
auto translated_event = event.translated(-m_active_input_tracking_window->position());
deliver_mouse_event(*m_active_input_tracking_window, translated_event, true);
if (event.type() == Event::MouseUp && event.buttons() == 0) {
m_active_input_tracking_window = nullptr;
}
hovered_window = m_window_stack.window_at(event.position());
return true;
}
void WindowManager::process_mouse_event(MouseEvent& event, Window*& hovered_window)
{
Window* received_mouse_event = nullptr;
hovered_window = nullptr;
if (process_ongoing_active_input_mouse_event(event, hovered_window))
return;
// We need to process ongoing drag events first. Otherwise, global tracking
// and dnd collides, leading to duplicate GUI::DragOperation instances
if (process_ongoing_drag(event, hovered_window))
return;
hovered_window = nullptr;
if (process_ongoing_window_move(event, hovered_window))
return;
@ -972,90 +994,73 @@ void WindowManager::process_mouse_event(MouseEvent& event, Window*& hovered_wind
}
Window* event_window_with_frame = nullptr;
Window* received_mouse_event = nullptr;
if (m_active_input_tracking_window) {
// At this point, we have delivered the start of an input sequence to a
// client application. We must keep delivering to that client
// application until the input sequence is done.
//
// This prevents e.g. moving on one window out of the bounds starting
// a move in that other unrelated window, and other silly shenanigans.
auto translated_event = event.translated(-m_active_input_tracking_window->position());
deliver_mouse_event(*m_active_input_tracking_window, translated_event, true);
received_mouse_event = m_active_input_tracking_window.ptr();
auto process_mouse_event_for_window = [&](Window& window) {
if (&window != m_resize_candidate.ptr())
clear_resize_candidate();
if (event.type() == Event::MouseUp && event.buttons() == 0) {
m_active_input_tracking_window = nullptr;
}
hovered_window = m_window_stack.window_at(event.position());
} else {
auto process_mouse_event_for_window = [&](Window& window) {
if (&window != m_resize_candidate.ptr())
clear_resize_candidate();
// First check if we should initiate a move or resize (Super+LMB or Super+RMB).
// In those cases, the event is swallowed by the window manager.
if (window.is_movable()) {
if (!window.is_fullscreen() && m_keyboard_modifiers == Mod_Super && event.type() == Event::MouseDown && event.button() == MouseButton::Left) {
hovered_window = &window;
start_window_move(window, event);
return;
}
if (window.is_resizable() && m_keyboard_modifiers == Mod_Super && event.type() == Event::MouseDown && event.button() == MouseButton::Right && !window.blocking_modal_window()) {
hovered_window = &window;
start_window_resize(window, event);
return;
}
}
VERIFY(window.hit_test(event.position()).has_value());
if (event.type() == Event::MouseDown) {
// We're clicking on something that's blocked by a modal window.
// Flash the modal window to let the user know about it.
if (auto* blocking_modal_window = window.blocking_modal_window())
blocking_modal_window->frame().start_flash_animation();
if (window.type() == WindowType::Normal || window.type() == WindowType::ToolWindow)
move_to_front_and_make_active(window);
else if (window.type() == WindowType::Desktop)
set_active_window(&window);
}
if (window.frame().hit_test(event.position()).has_value()) {
// We are hitting the frame, pass the event along to WindowFrame.
window.frame().handle_mouse_event(event.translated(-window.frame().rect().location()));
event_window_with_frame = &window;
} else if (window.hit_test(event.position(), false).has_value()) {
// We are hitting the window content
// First check if we should initiate a move or resize (Super+LMB or Super+RMB).
// In those cases, the event is swallowed by the window manager.
if (window.is_movable()) {
if (!window.is_fullscreen() && m_keyboard_modifiers == Mod_Super && event.type() == Event::MouseDown && event.button() == MouseButton::Left) {
hovered_window = &window;
if (!window.global_cursor_tracking() && !window.blocking_modal_window()) {
auto translated_event = event.translated(-window.position());
deliver_mouse_event(window, translated_event, true);
received_mouse_event = &window;
if (event.type() == Event::MouseDown) {
m_active_input_tracking_window = window;
}
}
start_window_move(window, event);
return;
}
if (window.is_resizable() && m_keyboard_modifiers == Mod_Super && event.type() == Event::MouseDown && event.button() == MouseButton::Right && !window.blocking_modal_window()) {
hovered_window = &window;
start_window_resize(window, event);
return;
}
};
if (auto* fullscreen_window = active_fullscreen_window()) {
process_mouse_event_for_window(*fullscreen_window);
} else {
m_window_stack.for_each_visible_window_from_front_to_back([&](Window& window) {
if (!window.hit_test(event.position()).has_value())
return IterationDecision::Continue;
process_mouse_event_for_window(window);
return IterationDecision::Break;
});
}
// Clicked outside of any window
if (!hovered_window && !event_window_with_frame && event.type() == Event::MouseDown)
set_active_window(nullptr);
VERIFY(window.hit_test(event.position()).has_value());
if (event.type() == Event::MouseDown) {
// We're clicking on something that's blocked by a modal window.
// Flash the modal window to let the user know about it.
if (auto* blocking_modal_window = window.blocking_modal_window())
blocking_modal_window->frame().start_flash_animation();
if (window.type() == WindowType::Normal || window.type() == WindowType::ToolWindow)
move_to_front_and_make_active(window);
else if (window.type() == WindowType::Desktop)
set_active_window(&window);
}
if (window.frame().hit_test(event.position()).has_value()) {
// We are hitting the frame, pass the event along to WindowFrame.
window.frame().handle_mouse_event(event.translated(-window.frame().rect().location()));
event_window_with_frame = &window;
} else if (window.hit_test(event.position(), false).has_value()) {
// We are hitting the window content
hovered_window = &window;
if (!window.global_cursor_tracking() && !window.blocking_modal_window()) {
auto translated_event = event.translated(-window.position());
deliver_mouse_event(window, translated_event, true);
received_mouse_event = &window;
if (event.type() == Event::MouseDown) {
m_active_input_tracking_window = window;
}
}
}
};
if (auto* fullscreen_window = active_fullscreen_window()) {
process_mouse_event_for_window(*fullscreen_window);
} else {
m_window_stack.for_each_visible_window_from_front_to_back([&](Window& window) {
if (!window.hit_test(event.position()).has_value())
return IterationDecision::Continue;
process_mouse_event_for_window(window);
return IterationDecision::Break;
});
}
// Clicked outside of any window
if (!hovered_window && !event_window_with_frame && event.type() == Event::MouseDown)
set_active_window(nullptr);
auto reverse_iterator = m_window_stack.windows().rbegin();
for (; reverse_iterator != m_window_stack.windows().rend(); ++reverse_iterator) {
auto& window = *reverse_iterator;
@ -1620,5 +1625,4 @@ void WindowManager::set_window_with_active_menu(Window* window)
else
m_window_with_active_menu = nullptr;
}
}

View file

@ -239,6 +239,7 @@ private:
bool process_ongoing_window_resize(MouseEvent const&, Window*& hovered_window);
bool process_ongoing_window_move(MouseEvent&, Window*& hovered_window);
bool process_ongoing_drag(MouseEvent&, Window*& hovered_window);
bool process_ongoing_active_input_mouse_event(MouseEvent&, Window*& hovered_window);
template<typename Callback>
void for_each_window_manager(Callback);