From 9f54ea9bcd81567f4250cdea4b4f21f249e6d7b4 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 16 Feb 2020 19:28:08 +0100 Subject: [PATCH] NotificationServer: Add a system service for desktop notifications This patch adds NotificationServer, which runs as the "notify" user and provides an IPC API for desktop notifications. LibGUI gains the GUI::Notification class for showing notifications. NotificationServer is spawned on demand and will unspawn after dimissing all visible notifications. :^) Finally, this also comes with a small /bin/notify utility. --- Base/etc/SystemServer.ini | 8 ++ Base/etc/group | 3 +- Base/etc/passwd | 1 + Kernel/build-root-filesystem.sh | 1 + Libraries/LibGUI/Makefile | 3 + Libraries/LibGUI/Notification.cpp | 48 +++++++++++ Libraries/LibGUI/Notification.h | 28 ++++++ .../NotificationServer/ClientConnection.cpp | 62 +++++++++++++ Servers/NotificationServer/ClientConnection.h | 49 +++++++++++ Servers/NotificationServer/Makefile | 24 ++++++ .../NotificationServer/NotificationClient.ipc | 4 + .../NotificationServer/NotificationServer.ipc | 7 ++ .../NotificationServer/NotificationWindow.cpp | 86 +++++++++++++++++++ .../NotificationServer/NotificationWindow.h | 43 ++++++++++ Servers/NotificationServer/main.cpp | 71 +++++++++++++++ Userland/notify.cpp | 49 +++++++++++ 16 files changed, 486 insertions(+), 1 deletion(-) create mode 100644 Libraries/LibGUI/Notification.cpp create mode 100644 Libraries/LibGUI/Notification.h create mode 100644 Servers/NotificationServer/ClientConnection.cpp create mode 100644 Servers/NotificationServer/ClientConnection.h create mode 100644 Servers/NotificationServer/Makefile create mode 100644 Servers/NotificationServer/NotificationClient.ipc create mode 100644 Servers/NotificationServer/NotificationServer.ipc create mode 100644 Servers/NotificationServer/NotificationWindow.cpp create mode 100644 Servers/NotificationServer/NotificationWindow.h create mode 100644 Servers/NotificationServer/main.cpp create mode 100644 Userland/notify.cpp diff --git a/Base/etc/SystemServer.ini b/Base/etc/SystemServer.ini index f5fd678a3a2..96f9349cee5 100644 --- a/Base/etc/SystemServer.ini +++ b/Base/etc/SystemServer.ini @@ -20,6 +20,14 @@ Priority=low KeepAlive=1 User=lookup +[NotificationServer] +Socket=/tmp/portal/notify +SocketPermissions=660 +Lazy=1 +Priority=low +KeepAlive=1 +User=notify + [WindowServer] Socket=/tmp/portal/window SocketPermissions=660 diff --git a/Base/etc/group b/Base/etc/group index 847883e3da0..d435cf62c8e 100644 --- a/Base/etc/group +++ b/Base/etc/group @@ -5,5 +5,6 @@ phys:x:3:window audio:x:4:anon lookup:x:10:protocol,anon protocol:x:11:anon -window:x:13:anon +notify:x:12:anon +window:x:13:anon,notify users:x:100:anon diff --git a/Base/etc/passwd b/Base/etc/passwd index 78f60742f24..b10404b60b5 100644 --- a/Base/etc/passwd +++ b/Base/etc/passwd @@ -1,6 +1,7 @@ root:x:0:0:root:/:/bin/sh lookup:x:10:10:LookupServer,,,:/:/bin/false protocol:x:11:11:ProtocolServer,,,:/:/bin/false +notify:x:12:12:NotificationServer,,,:/:/bin/false window:x:13:13:WindowServer,,,:/:/bin/false anon:x:100:100:Anonymous,,,:/home/anon:/bin/sh nona:x:200:200:Nona,,,:/home/nona:/bin/sh diff --git a/Kernel/build-root-filesystem.sh b/Kernel/build-root-filesystem.sh index 31daa843a6e..fedcaa3ee54 100755 --- a/Kernel/build-root-filesystem.sh +++ b/Kernel/build-root-filesystem.sh @@ -154,6 +154,7 @@ cp ../Servers/AudioServer/AudioServer mnt/bin/AudioServer cp ../Servers/TTYServer/TTYServer mnt/bin/TTYServer cp ../Servers/TelnetServer/TelnetServer mnt/bin/TelnetServer cp ../Servers/ProtocolServer/ProtocolServer mnt/bin/ProtocolServer +cp ../Servers/NotificationServer/NotificationServer mnt/bin/NotificationServer cp ../Servers/WebServer/WebServer mnt/bin/WebServer cp ../Shell/Shell mnt/bin/Shell cp ../MenuApplets/Audio/Audio.MenuApplet mnt/bin/ diff --git a/Libraries/LibGUI/Makefile b/Libraries/LibGUI/Makefile index 9ad544d6ffc..c9024f391eb 100644 --- a/Libraries/LibGUI/Makefile +++ b/Libraries/LibGUI/Makefile @@ -40,6 +40,7 @@ OBJS = \ Model.o \ ModelIndex.o \ ModelSelection.o \ + Notification.o \ Painter.o \ ProgressBar.o \ RadioButton.o \ @@ -71,6 +72,8 @@ LIBRARY = libgui.a Application.cpp: ../../Servers/WindowServer/WindowServerEndpoint.h +Notification.cpp: ../../Servers/NotificationServer/NotificationServerEndpoint.h + ../../Servers/WindowServer/WindowServerEndpoint.h: @flock $(dir $(@)) $(MAKE) -C $(dir $(@)) diff --git a/Libraries/LibGUI/Notification.cpp b/Libraries/LibGUI/Notification.cpp new file mode 100644 index 00000000000..a06598edd83 --- /dev/null +++ b/Libraries/LibGUI/Notification.cpp @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +namespace GUI { + +class NotificationServerConnection : public IPC::ServerConnection + , public NotificationClientEndpoint { + C_OBJECT(NotificationServerConnection) +public: + virtual void handshake() override + { + auto response = send_sync(); + set_my_client_id(response->client_id()); + } + +private: + NotificationServerConnection() + : IPC::ServerConnection(*this, "/tmp/portal/notify") + { + + } + virtual void handle(const Messages::NotificationClient::Dummy&) override {} +}; + +Notification::Notification() +{ +} + +Notification::~Notification() +{ +} + +static NotificationServerConnection& notification_server_connection() +{ + static NotificationServerConnection* connection; + if (!connection) + connection = &NotificationServerConnection::construct().leak_ref(); + return *connection; +} + +void Notification::show() +{ + notification_server_connection().post_message(Messages::NotificationServer::ShowNotification(m_text, m_title)); +} + +} diff --git a/Libraries/LibGUI/Notification.h b/Libraries/LibGUI/Notification.h new file mode 100644 index 00000000000..218dfa6c7e8 --- /dev/null +++ b/Libraries/LibGUI/Notification.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace GUI { + +class Notification : public Core::Object { + C_OBJECT(Notification); + +public: + virtual ~Notification() override; + + const String& text() const { return m_text; } + void set_text(const String& text) { m_text = text; } + + const String& title() const { return m_title; } + void set_title(const String& title) { m_title = title; } + + void show(); + +private: + Notification(); + + String m_title; + String m_text; +}; + +} diff --git a/Servers/NotificationServer/ClientConnection.cpp b/Servers/NotificationServer/ClientConnection.cpp new file mode 100644 index 00000000000..7ee8243caf1 --- /dev/null +++ b/Servers/NotificationServer/ClientConnection.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020, Andreas Kling + * 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 "NotificationClientEndpoint.h" +#include "NotificationWindow.h" +#include + +namespace NotificationServer { + +static HashMap> s_connections; + +ClientConnection::ClientConnection(Core::LocalSocket& client_socket, int client_id) + : IPC::ClientConnection(*this, client_socket, client_id) +{ + s_connections.set(client_id, *this); +} + +ClientConnection::~ClientConnection() +{ +} + +void ClientConnection::die() +{ + s_connections.remove(client_id()); +} + +OwnPtr ClientConnection::handle(const Messages::NotificationServer::Greet&) +{ + return make(client_id()); +} + +void ClientConnection::handle(const Messages::NotificationServer::ShowNotification& message) +{ + auto window = NotificationWindow::construct(message.text(), message.title()); + window->show(); +} + +} diff --git a/Servers/NotificationServer/ClientConnection.h b/Servers/NotificationServer/ClientConnection.h new file mode 100644 index 00000000000..93493679c87 --- /dev/null +++ b/Servers/NotificationServer/ClientConnection.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020, Andreas Kling + * 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 +#include + +namespace NotificationServer { + +class ClientConnection final : public IPC::ClientConnection + , public NotificationServerEndpoint { + C_OBJECT(ClientConnection) +public: + ~ClientConnection() override; + + virtual void die() override; + +private: + explicit ClientConnection(Core::LocalSocket&, int client_id); + + virtual OwnPtr handle(const Messages::NotificationServer::Greet&) override; + virtual void handle(const Messages::NotificationServer::ShowNotification&) override; +}; + +} diff --git a/Servers/NotificationServer/Makefile b/Servers/NotificationServer/Makefile new file mode 100644 index 00000000000..36a1e416528 --- /dev/null +++ b/Servers/NotificationServer/Makefile @@ -0,0 +1,24 @@ +OBJS = \ + main.o \ + ClientConnection.o \ + NotificationWindow.o + +PROGRAM = NotificationServer + +LIB_DEPS = GUI Gfx Core IPC + +EXTRA_CLEAN = NotificationServerEndpoint.h NotificationClientEndpoint.h + +*.cpp: NotificationServerEndpoint.h NotificationClientEndpoint.h + +NotificationServerEndpoint.h: NotificationServer.ipc | IPCCOMPILER + @echo "IPC $<"; $(IPCCOMPILER) $< > $@ + +NotificationClientEndpoint.h: NotificationClient.ipc | IPCCOMPILER + @echo "IPC $<"; $(IPCCOMPILER) $< > $@ + +install: + mkdir -p $(SERENITY_BASE_DIR)/Root/usr/include/NotificationServer/ + cp *.h $(SERENITY_BASE_DIR)/Root/usr/include/NotificationServer/ + +include ../../Makefile.common diff --git a/Servers/NotificationServer/NotificationClient.ipc b/Servers/NotificationServer/NotificationClient.ipc new file mode 100644 index 00000000000..fc85168f769 --- /dev/null +++ b/Servers/NotificationServer/NotificationClient.ipc @@ -0,0 +1,4 @@ +endpoint NotificationClient = 92 +{ + Dummy() =| +} diff --git a/Servers/NotificationServer/NotificationServer.ipc b/Servers/NotificationServer/NotificationServer.ipc new file mode 100644 index 00000000000..e8040e1f527 --- /dev/null +++ b/Servers/NotificationServer/NotificationServer.ipc @@ -0,0 +1,7 @@ +endpoint NotificationServer = 95 +{ + // Basic protocol + Greet() => (i32 client_id) + + ShowNotification(String text, String title) =| +} diff --git a/Servers/NotificationServer/NotificationWindow.cpp b/Servers/NotificationServer/NotificationWindow.cpp new file mode 100644 index 00000000000..1fca0803de4 --- /dev/null +++ b/Servers/NotificationServer/NotificationWindow.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2020, Andreas Kling + * 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 +#include +#include +#include +#include +#include +#include + +namespace NotificationServer { + +static HashTable> s_windows; + +NotificationWindow::NotificationWindow(const String& text, const String& title) +{ + s_windows.set(this); + + set_window_type(GUI::WindowType::Tooltip); + + Gfx::Rect rect; + rect.set_width(200); + rect.set_height(40); + rect.set_location(GUI::Desktop::the().rect().top_right().translated(-rect.width() - 8, 26)); + set_rect(rect); + + auto widget = GUI::Widget::construct(); + widget->set_fill_with_background_color(true); + + widget->set_layout(make()); + widget->layout()->set_margins({ 4, 4, 4, 4 }); + widget->layout()->set_spacing(4); + + auto left_container = GUI::Widget::construct(widget.ptr()); + left_container->set_layout(make()); + + auto title_label = GUI::Label::construct(title, left_container); + title_label->set_font(Gfx::Font::default_bold_font()); + title_label->set_text_alignment(Gfx::TextAlignment::CenterLeft); + auto text_label = GUI::Label::construct(text, left_container); + text_label->set_text_alignment(Gfx::TextAlignment::CenterLeft); + + auto right_container = GUI::Widget::construct(widget.ptr()); + right_container->set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fill); + right_container->set_preferred_size(40, 0); + right_container->set_layout(make()); + + auto button = GUI::Button::construct("Okay", right_container); + button->on_click = [this](auto&) { + s_windows.remove(this); + close(); + }; + + set_main_widget(widget); +} + +NotificationWindow::~NotificationWindow() +{ +} + +} diff --git a/Servers/NotificationServer/NotificationWindow.h b/Servers/NotificationServer/NotificationWindow.h new file mode 100644 index 00000000000..73f19e59b93 --- /dev/null +++ b/Servers/NotificationServer/NotificationWindow.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020, Andreas Kling + * 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 + +namespace NotificationServer { + +class NotificationWindow final : public GUI::Window { + C_OBJECT(NotificationWindow); + +public: + virtual ~NotificationWindow() override; + +private: + NotificationWindow(const String& text, const String& title); +}; + +} diff --git a/Servers/NotificationServer/main.cpp b/Servers/NotificationServer/main.cpp new file mode 100644 index 00000000000..f26aeec5674 --- /dev/null +++ b/Servers/NotificationServer/main.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020, Andreas Kling + * 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 +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + if (pledge("stdio shared_buffer accept rpath wpath cpath unix fattr", nullptr) < 0) { + perror("pledge"); + return 1; + } + + GUI::Application app(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) { + dbg() << "NotificationServer: accept failed."; + return; + } + static int s_next_client_id = 0; + int client_id = ++s_next_client_id; + IPC::new_client_connection(*client_socket, 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; + } + + return app.exec(); +} diff --git a/Userland/notify.cpp b/Userland/notify.cpp new file mode 100644 index 00000000000..a1e0176dba5 --- /dev/null +++ b/Userland/notify.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020, Andreas Kling + * 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 +#include +#include +#include + +int main(int argc, char** argv) +{ + GUI::Application app(argc, argv); + + Core::ArgsParser args_parser; + const char* title = nullptr; + const char* message = nullptr; + args_parser.add_positional_argument(title, "Title of the notification", "title"); + args_parser.add_positional_argument(message, "Message to display in the notification", "message"); + args_parser.parse(argc, argv); + + auto notification = GUI::Notification::construct(); + notification->set_text(message); + notification->set_title(title); + notification->show(); + + return 0; +}