diff options
author | Andreas Kling <kling@serenityos.org> | 2020-05-08 22:00:41 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-05-08 22:00:41 +0200 |
commit | 239fd334059823619e0af199e9b5618a320e15f9 (patch) | |
tree | 194b7e99e39295d91a21efa2c31d98743ebdad8b /Services/SystemMenu | |
parent | cf3b58fbe8f836c13e44d6152d78960aff6089ef (diff) | |
download | serenity-239fd334059823619e0af199e9b5618a320e15f9.zip |
Services: Move Taskbar and SystemMenu from Applications to Services
Diffstat (limited to 'Services/SystemMenu')
-rw-r--r-- | Services/SystemMenu/Makefile | 9 | ||||
-rw-r--r-- | Services/SystemMenu/PowerDialog.cpp | 119 | ||||
-rw-r--r-- | Services/SystemMenu/PowerDialog.h | 41 | ||||
-rw-r--r-- | Services/SystemMenu/main.cpp | 218 |
4 files changed, 387 insertions, 0 deletions
diff --git a/Services/SystemMenu/Makefile b/Services/SystemMenu/Makefile new file mode 100644 index 0000000000..58a9369e16 --- /dev/null +++ b/Services/SystemMenu/Makefile @@ -0,0 +1,9 @@ +OBJS = \ + main.o \ + PowerDialog.o + +PROGRAM = SystemMenu + +LIB_DEPS = GUI Gfx IPC Core + +include ../../Makefile.common diff --git a/Services/SystemMenu/PowerDialog.cpp b/Services/SystemMenu/PowerDialog.cpp new file mode 100644 index 0000000000..f1416a73fc --- /dev/null +++ b/Services/SystemMenu/PowerDialog.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * 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 <AK/String.h> +#include <AK/Vector.h> +#include <LibGUI/BoxLayout.h> +#include <LibGUI/Button.h> +#include <LibGUI/Desktop.h> +#include <LibGUI/Label.h> +#include <LibGUI/RadioButton.h> +#include <LibGUI/Widget.h> +#include <LibGfx/Font.h> + +#include "PowerDialog.h" + +struct PowerOption { + String title; + Vector<char const*> cmd; + bool enabled; + bool default_action; +}; + +static const Vector<PowerOption> options = { + { "Shut down", { "/bin/shutdown", "--now", nullptr }, true, true }, + { "Restart", { "/bin/reboot", nullptr }, true, false }, + { "Log out", {}, false, false }, + { "Sleep", {}, false, false }, +}; + +Vector<char const*> PowerDialog::show() +{ + auto rc = PowerDialog::construct()->exec(); + if(rc == ExecResult::ExecOK) + return options[rc].cmd; + + return {}; +} + +PowerDialog::PowerDialog() + : GUI::Dialog(nullptr) +{ + Gfx::Rect rect({ 0, 0, 180, 180 + ((static_cast<int>(options.size()) - 3) * 16) }); + rect.center_within(GUI::Desktop::the().rect()); + set_rect(rect); + set_resizable(false); + set_title("SerenityOS"); + set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/power.png")); + + auto& main = set_main_widget<GUI::Widget>(); + main.set_layout<GUI::VerticalBoxLayout>(); + main.layout()->set_margins({ 8, 8, 8, 8 }); + main.layout()->set_spacing(8); + main.set_fill_with_background_color(true); + + auto& header = main.add<GUI::Label>(); + header.set_text("What would you like to do?"); + header.set_preferred_size(0, 16); + header.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); + header.set_font(Gfx::Font::default_bold_font()); + + for (size_t i = 0; i < options.size(); i++) { + auto action = options[i]; + auto& radio = main.add<GUI::RadioButton>(); + radio.set_enabled(action.enabled); + radio.set_text(action.title); + + radio.on_checked = [this, i](auto) { + m_selected_option = i; + }; + + if (action.default_action) { + radio.set_checked(true); + m_selected_option = i; + } + } + + auto& button_box = main.add<GUI::Widget>(); + button_box.set_layout<GUI::HorizontalBoxLayout>(); + button_box.layout()->set_spacing(8); + + auto& ok_button = button_box.add<GUI::Button>(); + ok_button.on_click = [this] { + done(ExecResult::ExecOK); + }; + ok_button.set_text("OK"); + + auto& cancel_button = button_box.add<GUI::Button>(); + cancel_button.on_click = [this] { + done(ExecResult::ExecCancel); + }; + cancel_button.set_text("Cancel"); +} + +PowerDialog::~PowerDialog() +{ +} diff --git a/Services/SystemMenu/PowerDialog.h b/Services/SystemMenu/PowerDialog.h new file mode 100644 index 0000000000..1a1c18a8d3 --- /dev/null +++ b/Services/SystemMenu/PowerDialog.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * 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 <AK/Vector.h> +#include <LibCore/Object.h> +#include <LibGUI/Dialog.h> + +class PowerDialog : public GUI::Dialog { + C_OBJECT(PowerDialog) +public: + static Vector<char const*> show(); + +private: + PowerDialog(); + ~PowerDialog(); + + int m_selected_option { -1 }; +}; diff --git a/Services/SystemMenu/main.cpp b/Services/SystemMenu/main.cpp new file mode 100644 index 0000000000..483a8b8b6a --- /dev/null +++ b/Services/SystemMenu/main.cpp @@ -0,0 +1,218 @@ +/* + * 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 "PowerDialog.h" +#include <AK/FileSystemPath.h> +#include <AK/QuickSort.h> +#include <LibCore/ConfigFile.h> +#include <LibCore/DirIterator.h> +#include <LibCore/StandardPaths.h> +#include <LibGUI/Action.h> +#include <LibGUI/ActionGroup.h> +#include <LibGUI/Application.h> +#include <LibGUI/Menu.h> +#include <LibGUI/WindowServerConnection.h> +#include <LibGfx/Bitmap.h> + +//#define SYSTEM_MENU_DEBUG + +struct AppMetadata { + String executable; + String name; + String icon_path; + String category; +}; +Vector<AppMetadata> g_apps; + +HashMap<String, NonnullRefPtr<GUI::Menu>> g_app_category_menus; + +struct ThemeMetadata { + String name; + String path; +}; + +Color g_menu_selection_color; + +Vector<ThemeMetadata> g_themes; +RefPtr<GUI::Menu> g_themes_menu; +GUI::ActionGroup g_themes_group; + +static NonnullRefPtr<GUI::Menu> build_system_menu(); + +int main(int argc, char** argv) +{ + GUI::Application app(argc, argv); + + auto menu = build_system_menu(); + menu->realize_menu_if_needed(); + + GUI::WindowServerConnection::the().send_sync<Messages::WindowServer::SetSystemMenu>(menu->menu_id()); + + if (pledge("stdio shared_buffer accept rpath proc exec", nullptr) < 0) { + perror("pledge"); + return 1; + } + + if (chdir(Core::StandardPaths::home_directory().characters()) < 0) { + perror("chdir"); + return 1; + } + + if (unveil("/bin", "x")) { + perror("unveil"); + return 1; + } + + if (unveil("/res", "r")) { + perror("unveil"); + return 1; + } + + unveil(nullptr, nullptr); + + return app.exec(); +} + +NonnullRefPtr<GUI::Menu> build_system_menu() +{ + HashTable<String> seen_app_categories; + { + Core::DirIterator dt("/res/apps", Core::DirIterator::SkipDots); + while (dt.has_next()) { + auto af_name = dt.next_path(); + auto af_path = String::format("/res/apps/%s", af_name.characters()); + auto af = Core::ConfigFile::open(af_path); + if (!af->has_key("App", "Name") || !af->has_key("App", "Executable")) + continue; + auto app_name = af->read_entry("App", "Name"); + auto app_executable = af->read_entry("App", "Executable"); + auto app_category = af->read_entry("App", "Category"); + auto app_icon_path = af->read_entry("Icons", "16x16"); + g_apps.append({ app_executable, app_name, app_icon_path, app_category }); + seen_app_categories.set(app_category); + } + quick_sort(g_apps, [](auto& a, auto& b) { return a.name < b.name; }); + } + + Vector<String> sorted_app_categories; + for (auto& category : seen_app_categories) + sorted_app_categories.append(category); + quick_sort(sorted_app_categories); + + auto system_menu = GUI::Menu::construct("\xE2\x9A\xA1"); // HIGH VOLTAGE SIGN + + // First we construct all the necessary app category submenus. + for (const auto& category : sorted_app_categories) { + + if (g_app_category_menus.contains(category)) + continue; + auto& category_menu = system_menu->add_submenu(category); + g_app_category_menus.set(category, category_menu); + } + + // Then we create and insert all the app menu items into the right place. + int app_identifier = 0; + for (const auto& app : g_apps) { + RefPtr<Gfx::Bitmap> icon; + if (!app.icon_path.is_empty()) + icon = Gfx::Bitmap::load_from_file(app.icon_path); + +#ifdef SYSTEM_MENU_DEBUG + if (icon) + dbg() << "App " << app.name << " has icon with size " << icon->size(); +#endif + + auto parent_menu = g_app_category_menus.get(app.category).value_or(*system_menu); + parent_menu->add_action(GUI::Action::create(app.name, icon.ptr(), [app_identifier](auto&) { + dbg() << "Activated app with ID " << app_identifier; + if (fork() == 0) { + const auto& bin = g_apps[app_identifier].executable; + if (execl(bin.characters(), bin.characters(), nullptr) < 0) + perror("execl"); + ASSERT_NOT_REACHED(); + } + })); + ++app_identifier; + } + + system_menu->add_separator(); + + g_themes_group.set_exclusive(true); + g_themes_group.set_unchecking_allowed(false); + + g_themes_menu = &system_menu->add_submenu("Themes"); + + { + Core::DirIterator dt("/res/themes", Core::DirIterator::SkipDots); + while (dt.has_next()) { + auto theme_name = dt.next_path(); + auto theme_path = String::format("/res/themes/%s", theme_name.characters()); + g_themes.append({ FileSystemPath(theme_name).title(), theme_path }); + } + quick_sort(g_themes, [](auto& a, auto& b) { return a.name < b.name; }); + } + + auto current_theme_name = GUI::WindowServerConnection::the().send_sync<Messages::WindowServer::GetSystemTheme>()->theme_name(); + + { + int theme_identifier = 0; + for (auto& theme : g_themes) { + auto action = GUI::Action::create_checkable(theme.name, [theme_identifier](auto&) { + auto& theme = g_themes[theme_identifier]; + dbg() << "Theme switched to " << theme.name << " at path " << theme.path; + auto response = GUI::WindowServerConnection::the().send_sync<Messages::WindowServer::SetSystemTheme>(theme.path, theme.name); + ASSERT(response->success()); + }); + if (theme.name == current_theme_name) + action->set_checked(true); + g_themes_group.add_action(action); + g_themes_menu->add_action(action); + ++theme_identifier; + } + } + + system_menu->add_separator(); + system_menu->add_action(GUI::Action::create("About...", Gfx::Bitmap::load_from_file("/res/icons/16x16/ladybug.png"), [](auto&) { + if (fork() == 0) { + execl("/bin/About", "/bin/About", nullptr); + ASSERT_NOT_REACHED(); + } + })); + system_menu->add_separator(); + system_menu->add_action(GUI::Action::create("Exit...", [](auto&) { + Vector<char const*> command = PowerDialog::show(); + + if (command.size() == 0) + return; + + if (fork() == 0) { + execv(command[0], const_cast<char* const*>(command.data())); + ASSERT_NOT_REACHED(); + } + })); + + return system_menu; +} |