From 43304d2adf896539f55ec337fa3cb1463933c353 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 19 Mar 2019 00:52:39 +0100 Subject: [PATCH] WindowServer: Add special treatment for modal windows. While a WSClientConnection has a modal window showing, non-modal windows belonging to that client are not sent any events. --- AK/Types.h | 2 ++ Applications/IRCClient/IRCAppWindow.cpp | 6 +++--- LibGUI/GWindow.cpp | 2 ++ LibGUI/GWindow.h | 1 + WindowServer/WSAPITypes.h | 8 ++------ WindowServer/WSClientConnection.cpp | 13 ++++++++++++- WindowServer/WSClientConnection.h | 25 +++++++++++++++++++++++++ WindowServer/WSMessage.h | 8 +++++++- WindowServer/WSMessageLoop.cpp | 2 +- WindowServer/WSWindow.cpp | 19 +++++++++++++++++-- WindowServer/WSWindow.h | 11 ++++++++++- WindowServer/WSWindowManager.cpp | 8 +++++++- WindowServer/WSWindowManager.h | 1 - 13 files changed, 89 insertions(+), 17 deletions(-) diff --git a/AK/Types.h b/AK/Types.h index 39f9fa5284..050d4930cc 100644 --- a/AK/Types.h +++ b/AK/Types.h @@ -48,6 +48,8 @@ constexpr unsigned KB = 1024; constexpr unsigned MB = KB * KB; constexpr unsigned GB = KB * KB * KB; +enum class IterationDecision { Continue, Abort }; + namespace std { typedef decltype(nullptr) nullptr_t; } diff --git a/Applications/IRCClient/IRCAppWindow.cpp b/Applications/IRCClient/IRCAppWindow.cpp index 1b431ecbd6..e70fe7a2a6 100644 --- a/Applications/IRCClient/IRCAppWindow.cpp +++ b/Applications/IRCClient/IRCAppWindow.cpp @@ -41,9 +41,6 @@ void IRCAppWindow::setup_client() }; m_client.on_connect = [this] { - GMessageBox box("We are connected!", "Message"); - int code = box.exec(); - dbgprintf("GMessageBox::exec() returned %d\n", code); m_client.join_channel("#test"); }; @@ -62,6 +59,9 @@ void IRCAppWindow::setup_actions() m_whois_action = GAction::create("Whois user", GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/16x16/irc-whois.rgb", { 16, 16 }), [] (auto&) { printf("FIXME: Implement whois action\n"); + GMessageBox box("Who would you like to WHOIS?", "Whois user"); + int code = box.exec(); + dbgprintf("GMessageBox::exec() returned %d\n", code); }); m_open_query_action = GAction::create("Open query", GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/16x16/irc-open-query.rgb", { 16, 16 }), [] (auto&) { diff --git a/LibGUI/GWindow.cpp b/LibGUI/GWindow.cpp index 3dd4b9557a..0001766406 100644 --- a/LibGUI/GWindow.cpp +++ b/LibGUI/GWindow.cpp @@ -59,6 +59,8 @@ void GWindow::show() request.window_id = m_window_id; request.window.rect = m_rect_when_windowless; request.window.has_alpha_channel = m_has_alpha_channel; + request.window.modal = m_modal; + request.window.resizable = m_resizable; request.window.opacity = m_opacity_when_windowless; request.window.size_increment = m_size_increment; request.window.base_size = m_base_size; diff --git a/LibGUI/GWindow.h b/LibGUI/GWindow.h index 3874887c5f..1331925678 100644 --- a/LibGUI/GWindow.h +++ b/LibGUI/GWindow.h @@ -105,5 +105,6 @@ private: bool m_has_alpha_channel { false }; bool m_double_buffering_enabled { true }; bool m_modal { false }; + bool m_resizable { true }; }; diff --git a/WindowServer/WSAPITypes.h b/WindowServer/WSAPITypes.h index 65dce5e0af..69f33c9612 100644 --- a/WindowServer/WSAPITypes.h +++ b/WindowServer/WSAPITypes.h @@ -3,12 +3,6 @@ #include #include -// GUI system call API types. - -struct WSAPI_WindowFlags { enum { - Visible = 1 << 0, -}; }; - typedef unsigned WSAPI_Color; struct WSAPI_Point { @@ -186,6 +180,8 @@ struct WSAPI_ClientMessage { struct { WSAPI_Rect rect; bool has_alpha_channel; + bool modal; + bool resizable; float opacity; WSAPI_Size base_size; WSAPI_Size size_increment; diff --git a/WindowServer/WSClientConnection.cpp b/WindowServer/WSClientConnection.cpp index f8f00db758..b3b9c9ac51 100644 --- a/WindowServer/WSClientConnection.cpp +++ b/WindowServer/WSClientConnection.cpp @@ -350,8 +350,9 @@ void WSClientConnection::handle_request(WSAPIGetClipboardContentsRequest&) void WSClientConnection::handle_request(WSAPICreateWindowRequest& request) { int window_id = m_next_window_id++; - auto window = make(*this, window_id); + auto window = make(*this, window_id, request.is_modal()); window->set_has_alpha_channel(request.has_alpha_channel()); + window->set_resizable(request.is_resizable()); window->set_title(request.title()); window->set_rect(request.rect()); window->set_opacity(request.opacity()); @@ -531,3 +532,13 @@ void WSClientConnection::on_request(WSAPIClientRequest& request) break; } } + +bool WSClientConnection::is_showing_modal_window() const +{ + for (auto& it : m_windows) { + auto& window = *it.value; + if (window.is_visible() && window.is_modal()) + return true; + } + return false; +} diff --git a/WindowServer/WSClientConnection.h b/WindowServer/WSClientConnection.h index e23f0cc0e1..06e643ed37 100644 --- a/WindowServer/WSClientConnection.h +++ b/WindowServer/WSClientConnection.h @@ -30,6 +30,11 @@ public: int fd() const { return m_fd; } pid_t pid() const { return m_pid; } + bool is_showing_modal_window() const; + + template void for_each_window_matching(Matching, Callback); + template void for_each_window(Callback); + private: virtual void on_message(WSMessage&) override; @@ -74,3 +79,23 @@ private: RetainPtr m_last_sent_clipboard_content; }; + +template +void WSClientConnection::for_each_window_matching(Matching matching, Callback callback) +{ + for (auto& it : m_windows) { + if (matching(*it.value)) { + if (callback(*it.value) == IterationDecision::Abort) + return; + } + } +} + +template +void WSClientConnection::for_each_window(Callback callback) +{ + for (auto& it : m_windows) { + if (callback(*it.value) == IterationDecision::Abort) + return; + } +} diff --git a/WindowServer/WSMessage.h b/WindowServer/WSMessage.h index 0ef6838a48..1b2ec1018c 100644 --- a/WindowServer/WSMessage.h +++ b/WindowServer/WSMessage.h @@ -392,12 +392,14 @@ private: class WSAPICreateWindowRequest : public WSAPIClientRequest { public: - WSAPICreateWindowRequest(int client_id, const Rect& rect, const String& title, bool has_alpha_channel, float opacity, const Size& base_size, const Size& size_increment) + WSAPICreateWindowRequest(int client_id, const Rect& rect, const String& title, bool has_alpha_channel, bool modal, bool resizable, float opacity, const Size& base_size, const Size& size_increment) : WSAPIClientRequest(WSMessage::APICreateWindowRequest, client_id) , m_rect(rect) , m_title(title) , m_opacity(opacity) , m_has_alpha_channel(has_alpha_channel) + , m_modal(modal) + , m_resizable(resizable) , m_size_increment(size_increment) , m_base_size(base_size) { @@ -406,6 +408,8 @@ public: Rect rect() const { return m_rect; } String title() const { return m_title; } bool has_alpha_channel() const { return m_has_alpha_channel; } + bool is_modal() const { return m_modal; } + bool is_resizable() const { return m_resizable; } float opacity() const { return m_opacity; } Size size_increment() const { return m_size_increment; } Size base_size() const { return m_base_size; } @@ -415,6 +419,8 @@ private: String m_title; float m_opacity { 0 }; bool m_has_alpha_channel { false }; + bool m_modal { false }; + bool m_resizable { false }; Size m_size_increment; Size m_base_size; }; diff --git a/WindowServer/WSMessageLoop.cpp b/WindowServer/WSMessageLoop.cpp index f7283d4732..90e357a38c 100644 --- a/WindowServer/WSMessageLoop.cpp +++ b/WindowServer/WSMessageLoop.cpp @@ -286,7 +286,7 @@ void WSMessageLoop::on_receive_from_client(int client_id, const WSAPI_ClientMess break; case WSAPI_ClientMessage::Type::CreateWindow: ASSERT(message.text_length < (ssize_t)sizeof(message.text)); - post_message(client, make(client_id, message.window.rect, String(message.text, message.text_length), message.window.has_alpha_channel, message.window.opacity, message.window.base_size, message.window.size_increment)); + post_message(client, make(client_id, message.window.rect, String(message.text, message.text_length), message.window.has_alpha_channel, message.window.modal, message.window.resizable, message.window.opacity, message.window.base_size, message.window.size_increment)); break; case WSAPI_ClientMessage::Type::DestroyWindow: post_message(client, make(client_id, message.window_id)); diff --git a/WindowServer/WSWindow.cpp b/WindowServer/WSWindow.cpp index 28b2816a40..c2e1435efb 100644 --- a/WindowServer/WSWindow.cpp +++ b/WindowServer/WSWindow.cpp @@ -21,9 +21,10 @@ WSWindow::WSWindow(WSMessageReceiver& internal_owner, WSWindowType type) WSWindowManager::the().add_window(*this); } -WSWindow::WSWindow(WSClientConnection& client, int window_id) +WSWindow::WSWindow(WSClientConnection& client, int window_id, bool modal) : m_client(&client) , m_type(WSWindowType::Normal) + , m_modal(modal) , m_window_id(window_id) , m_icon(default_window_icon()) { @@ -73,6 +74,9 @@ void WSWindow::on_message(WSMessage& message) if (m_internal_owner) return m_internal_owner->on_message(message); + if (is_blocked_by_modal_window()) + return; + WSAPI_ServerMessage server_message; server_message.window_id = window_id(); @@ -137,7 +141,6 @@ void WSWindow::on_message(WSMessage& message) if (server_message.type == WSAPI_ServerMessage::Type::Invalid) return; - ASSERT(m_client); m_client->post_message(server_message); } @@ -154,6 +157,13 @@ void WSWindow::set_visible(bool b) invalidate(); } +void WSWindow::set_resizable(bool resizable) +{ + if (m_resizable == resizable) + return; + m_resizable = resizable; +} + void WSWindow::invalidate() { WSWindowManager::the().invalidate(*this); @@ -163,3 +173,8 @@ bool WSWindow::is_active() const { return WSWindowManager::the().active_window() == this; } + +bool WSWindow::is_blocked_by_modal_window() const +{ + return !is_modal() && client() && client()->is_showing_modal_window(); +} diff --git a/WindowServer/WSWindow.h b/WindowServer/WSWindow.h index dc116ccdf7..ff6ef6b5c8 100644 --- a/WindowServer/WSWindow.h +++ b/WindowServer/WSWindow.h @@ -12,10 +12,12 @@ class WSMenu; class WSWindow final : public WSMessageReceiver, public InlineLinkedListNode { public: - WSWindow(WSClientConnection&, int window_id); + WSWindow(WSClientConnection&, int window_id, bool modal); WSWindow(WSMessageReceiver&, WSWindowType); virtual ~WSWindow() override; + bool is_blocked_by_modal_window() const; + WSClientConnection* client() { return m_client; } const WSClientConnection* client() const { return m_client; } @@ -38,6 +40,11 @@ public: bool is_visible() const { return m_visible; } void set_visible(bool); + bool is_modal() const { return m_modal; } + + bool is_resizable() const { return m_resizable; } + void set_resizable(bool); + Rect rect() const { return m_rect; } void set_rect(const Rect&); void set_rect(int x, int y, int width, int height) { set_rect({ x, y, width, height }); } @@ -106,6 +113,8 @@ private: bool m_visible { true }; bool m_has_alpha_channel { false }; bool m_has_painted_since_last_resize { false }; + bool m_modal { false }; + bool m_resizable { false }; RetainPtr m_backing_store; RetainPtr m_last_backing_store; int m_window_id { -1 }; diff --git a/WindowServer/WSWindowManager.cpp b/WindowServer/WSWindowManager.cpp index db9f53cabd..ef4a31a7d8 100644 --- a/WindowServer/WSWindowManager.cpp +++ b/WindowServer/WSWindowManager.cpp @@ -512,7 +512,7 @@ void WSWindowManager::add_window(WSWindow& window) { m_windows.set(&window); m_windows_in_order.append(&window); - if (!active_window()) + if (!active_window() || active_window()->client() == window.client()) set_active_window(&window); if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher) m_switcher.refresh(); @@ -520,6 +520,9 @@ void WSWindowManager::add_window(WSWindow& window) void WSWindowManager::move_to_front(WSWindow& window) { + if (window.is_blocked_by_modal_window()) + return; + if (m_windows_in_order.tail() != &window) invalidate(window); m_windows_in_order.remove(&window); @@ -1095,6 +1098,9 @@ void WSWindowManager::set_highlight_window(WSWindow* window) void WSWindowManager::set_active_window(WSWindow* window) { + if (window && window->is_blocked_by_modal_window()) + return; + if (window->type() != WSWindowType::Normal) { dbgprintf("WSWindowManager: Attempted to make a non-normal window active.\n"); return; diff --git a/WindowServer/WSWindowManager.h b/WindowServer/WSWindowManager.h index 1ec12cbede..55fbbdad31 100644 --- a/WindowServer/WSWindowManager.h +++ b/WindowServer/WSWindowManager.h @@ -26,7 +26,6 @@ class WSWindowSwitcher; class CharacterBitmap; class GraphicsBitmap; -enum class IterationDecision { Continue, Abort }; enum class ResizeDirection { None, Left, UpLeft, Up, UpRight, Right, DownRight, Down, DownLeft }; class WSWindowManager : public WSMessageReceiver {