diff options
Diffstat (limited to 'Userland/Services/NotificationServer')
8 files changed, 382 insertions, 0 deletions
diff --git a/Userland/Services/NotificationServer/CMakeLists.txt b/Userland/Services/NotificationServer/CMakeLists.txt new file mode 100644 index 0000000000..a28d9f16a5 --- /dev/null +++ b/Userland/Services/NotificationServer/CMakeLists.txt @@ -0,0 +1,13 @@ +compile_ipc(NotificationServer.ipc NotificationServerEndpoint.h) +compile_ipc(NotificationClient.ipc NotificationClientEndpoint.h) + +set(SOURCES + ClientConnection.cpp + main.cpp + NotificationWindow.cpp + NotificationServerEndpoint.h + NotificationClientEndpoint.h +) + +serenity_bin(NotificationServer) +target_link_libraries(NotificationServer LibGUI LibIPC) diff --git a/Userland/Services/NotificationServer/ClientConnection.cpp b/Userland/Services/NotificationServer/ClientConnection.cpp new file mode 100644 index 0000000000..9c6cc8051a --- /dev/null +++ b/Userland/Services/NotificationServer/ClientConnection.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ClientConnection.h" +#include "NotificationWindow.h" +#include <AK/HashMap.h> +#include <NotificationServer/NotificationClientEndpoint.h> + +namespace NotificationServer { + +static HashMap<int, RefPtr<ClientConnection>> s_connections; + +ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> client_socket, int client_id) + : IPC::ClientConnection<NotificationClientEndpoint, NotificationServerEndpoint>(*this, move(client_socket), client_id) +{ + s_connections.set(client_id, *this); +} + +ClientConnection::~ClientConnection() +{ +} + +void ClientConnection::die() +{ + s_connections.remove(client_id()); +} + +OwnPtr<Messages::NotificationServer::GreetResponse> ClientConnection::handle(const Messages::NotificationServer::Greet&) +{ + return make<Messages::NotificationServer::GreetResponse>(client_id()); +} + +OwnPtr<Messages::NotificationServer::ShowNotificationResponse> ClientConnection::handle(const Messages::NotificationServer::ShowNotification& message) +{ + auto window = NotificationWindow::construct(message.text(), message.title(), message.icon()); + window->show(); + return make<Messages::NotificationServer::ShowNotificationResponse>(); +} + +} diff --git a/Userland/Services/NotificationServer/ClientConnection.h b/Userland/Services/NotificationServer/ClientConnection.h new file mode 100644 index 0000000000..af4e36d0aa --- /dev/null +++ b/Userland/Services/NotificationServer/ClientConnection.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <LibIPC/ClientConnection.h> +#include <NotificationServer/NotificationClientEndpoint.h> +#include <NotificationServer/NotificationServerEndpoint.h> + +namespace NotificationServer { + +class ClientConnection final : public IPC::ClientConnection<NotificationClientEndpoint, NotificationServerEndpoint> + , public NotificationServerEndpoint { + C_OBJECT(ClientConnection) +public: + ~ClientConnection() override; + + virtual void die() override; + +private: + explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id); + + virtual OwnPtr<Messages::NotificationServer::GreetResponse> handle(const Messages::NotificationServer::Greet&) override; + virtual OwnPtr<Messages::NotificationServer::ShowNotificationResponse> handle(const Messages::NotificationServer::ShowNotification&) override; +}; + +} diff --git a/Userland/Services/NotificationServer/NotificationClient.ipc b/Userland/Services/NotificationServer/NotificationClient.ipc new file mode 100644 index 0000000000..fc85168f76 --- /dev/null +++ b/Userland/Services/NotificationServer/NotificationClient.ipc @@ -0,0 +1,4 @@ +endpoint NotificationClient = 92 +{ + Dummy() =| +} diff --git a/Userland/Services/NotificationServer/NotificationServer.ipc b/Userland/Services/NotificationServer/NotificationServer.ipc new file mode 100644 index 0000000000..e342ebf2d3 --- /dev/null +++ b/Userland/Services/NotificationServer/NotificationServer.ipc @@ -0,0 +1,7 @@ +endpoint NotificationServer = 95 +{ + // Basic protocol + Greet() => (i32 client_id) + + ShowNotification([UTF8] String text, [UTF8] String title, Gfx::ShareableBitmap icon) => () +} diff --git a/Userland/Services/NotificationServer/NotificationWindow.cpp b/Userland/Services/NotificationServer/NotificationWindow.cpp new file mode 100644 index 0000000000..e5739b1c3b --- /dev/null +++ b/Userland/Services/NotificationServer/NotificationWindow.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "NotificationWindow.h" +#include <AK/Vector.h> +#include <LibGUI/BoxLayout.h> +#include <LibGUI/Button.h> +#include <LibGUI/Desktop.h> +#include <LibGUI/ImageWidget.h> +#include <LibGUI/Label.h> +#include <LibGUI/Widget.h> +#include <LibGfx/Bitmap.h> +#include <LibGfx/Font.h> +#include <LibGfx/FontDatabase.h> +#include <LibGfx/ShareableBitmap.h> + +namespace NotificationServer { + +static Vector<RefPtr<NotificationWindow>> s_windows; + +void update_notification_window_locations() +{ + Gfx::IntRect last_window_rect; + for (auto& window : s_windows) { + Gfx::IntPoint new_window_location; + if (last_window_rect.is_null()) + new_window_location = GUI::Desktop::the().rect().top_right().translated(-window->rect().width() - 24, 26); + else + new_window_location = last_window_rect.bottom_left().translated(0, 10); + if (window->rect().location() != new_window_location) { + window->move_to(new_window_location); + window->set_original_rect(window->rect()); + } + last_window_rect = window->rect(); + } +} + +NotificationWindow::NotificationWindow(const String& text, const String& title, const Gfx::ShareableBitmap& icon) +{ + s_windows.append(this); + + set_window_type(GUI::WindowType::Notification); + set_resizable(false); + set_minimizable(false); + + Gfx::IntRect lowest_notification_rect_on_screen; + for (auto& window : s_windows) { + if (window->m_original_rect.y() > lowest_notification_rect_on_screen.y()) + lowest_notification_rect_on_screen = window->m_original_rect; + } + + Gfx::IntRect rect; + rect.set_width(220); + rect.set_height(40); + rect.set_location(GUI::Desktop::the().rect().top_right().translated(-rect.width() - 24, 26)); + + if (!lowest_notification_rect_on_screen.is_null()) + rect.set_location(lowest_notification_rect_on_screen.bottom_left().translated(0, 10)); + + set_rect(rect); + + m_original_rect = rect; + + auto& widget = set_main_widget<GUI::Widget>(); + widget.set_fill_with_background_color(true); + + widget.set_layout<GUI::HorizontalBoxLayout>(); + widget.layout()->set_margins({ 8, 8, 8, 8 }); + widget.layout()->set_spacing(6); + + if (icon.is_valid()) { + auto& image = widget.add<GUI::ImageWidget>(); + image.set_bitmap(icon.bitmap()); + } + + auto& left_container = widget.add<GUI::Widget>(); + left_container.set_layout<GUI::VerticalBoxLayout>(); + + auto& title_label = left_container.add<GUI::Label>(title); + title_label.set_font(Gfx::FontDatabase::default_bold_font()); + title_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); + auto& text_label = left_container.add<GUI::Label>(text); + text_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); + + auto& right_container = widget.add<GUI::Widget>(); + right_container.set_fixed_width(36); + right_container.set_layout<GUI::HorizontalBoxLayout>(); + + on_close_request = [this] { + s_windows.remove_first_matching([this](auto& entry) { return entry == this; }); + update_notification_window_locations(); + return CloseRequestDecision::Close; + }; +} + +NotificationWindow::~NotificationWindow() +{ +} + +} diff --git a/Userland/Services/NotificationServer/NotificationWindow.h b/Userland/Services/NotificationServer/NotificationWindow.h new file mode 100644 index 0000000000..607365fe6e --- /dev/null +++ b/Userland/Services/NotificationServer/NotificationWindow.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <LibGUI/Window.h> + +namespace NotificationServer { + +void update_notification_window_locations(); + +class NotificationWindow final : public GUI::Window { + C_OBJECT(NotificationWindow); + +public: + virtual ~NotificationWindow() override; + void set_original_rect(Gfx::IntRect original_rect) { m_original_rect = original_rect; }; + +private: + NotificationWindow(const String& text, const String& title, const Gfx::ShareableBitmap&); + + Gfx::IntRect m_original_rect; +}; + +} diff --git a/Userland/Services/NotificationServer/main.cpp b/Userland/Services/NotificationServer/main.cpp new file mode 100644 index 0000000000..91cd06e0d7 --- /dev/null +++ b/Userland/Services/NotificationServer/main.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ClientConnection.h" +#include "NotificationWindow.h" +#include <LibCore/LocalServer.h> +#include <LibGUI/Application.h> +#include <LibGUI/Desktop.h> +#include <LibGUI/WindowServerConnection.h> +#include <stdio.h> +#include <unistd.h> + +int main(int argc, char** argv) +{ + if (pledge("stdio shared_buffer accept rpath wpath cpath unix fattr", nullptr) < 0) { + perror("pledge"); + return 1; + } + + auto app = GUI::Application::construct(argc, argv); + auto server = Core::LocalServer::construct(); + + bool ok = server->take_over_from_system_server(); + ASSERT(ok); + server->on_ready_to_accept = [&] { + auto client_socket = server->accept(); + if (!client_socket) { + dbgln("NotificationServer: accept failed."); + return; + } + static int s_next_client_id = 0; + int client_id = ++s_next_client_id; + IPC::new_client_connection<NotificationServer::ClientConnection>(client_socket.release_nonnull(), client_id); + }; + + if (unveil("/res", "r") < 0) { + perror("unveil"); + return 1; + } + + unveil(nullptr, nullptr); + + if (pledge("stdio shared_buffer accept rpath", nullptr) < 0) { + perror("pledge"); + return 1; + } + + GUI::Desktop::the().on_rect_change = [](auto&) { NotificationServer::update_notification_window_locations(); }; + + return app->exec(); +} |