diff --git a/Userland/Libraries/LibGUI/ConnectionToWindowManagerServer.cpp b/Userland/Libraries/LibGUI/ConnectionToWindowManagerServer.cpp index 31bde9f50c..81c29a8568 100644 --- a/Userland/Libraries/LibGUI/ConnectionToWindowManagerServer.cpp +++ b/Userland/Libraries/LibGUI/ConnectionToWindowManagerServer.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, the SerenityOS developers. + * Copyright (c) 2023, David Ganz * * SPDX-License-Identifier: BSD-2-Clause */ @@ -87,4 +88,10 @@ void ConnectionToWindowManagerServer::keymap_changed(i32 wm_id, DeprecatedString Core::EventLoop::current().post_event(*window, make(wm_id, keymap)); } +void ConnectionToWindowManagerServer::add_to_quick_launch(i32 wm_id, pid_t pid) +{ + if (auto* window = Window::from_window_id(wm_id)) + Core::EventLoop::current().post_event(*window, make(wm_id, pid)); +} + } diff --git a/Userland/Libraries/LibGUI/ConnectionToWindowManagerServer.h b/Userland/Libraries/LibGUI/ConnectionToWindowManagerServer.h index 93300f0918..bee183c2ec 100644 --- a/Userland/Libraries/LibGUI/ConnectionToWindowManagerServer.h +++ b/Userland/Libraries/LibGUI/ConnectionToWindowManagerServer.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, the SerenityOS developers. + * Copyright (c) 2023, David Ganz * * SPDX-License-Identifier: BSD-2-Clause */ @@ -38,6 +39,7 @@ private: virtual void super_digit_key_pressed(i32, u8) override; virtual void workspace_changed(i32, u32, u32) override; virtual void keymap_changed(i32, DeprecatedString const&) override; + virtual void add_to_quick_launch(i32, pid_t) override; }; } diff --git a/Userland/Libraries/LibGUI/Event.h b/Userland/Libraries/LibGUI/Event.h index 82195b9d50..2c2e574412 100644 --- a/Userland/Libraries/LibGUI/Event.h +++ b/Userland/Libraries/LibGUI/Event.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2018-2023, Andreas Kling * Copyright (c) 2022, the SerenityOS developers. + * Copyright (c) 2023, David Ganz * * SPDX-License-Identifier: BSD-2-Clause */ @@ -73,6 +74,7 @@ public: WM_SuperDigitKeyPressed, WM_WorkspaceChanged, WM_KeymapChanged, + WM_AddToQuickLaunch, __End_WM_Events, }; @@ -264,6 +266,20 @@ private: const DeprecatedString m_keymap; }; +class WMAddToQuickLaunchEvent : public WMEvent { +public: + explicit WMAddToQuickLaunchEvent(int client_id, pid_t pid) + : WMEvent(Event::Type::WM_AddToQuickLaunch, client_id, 0) + , m_pid(pid) + { + } + + pid_t pid() const { return m_pid; } + +private: + pid_t m_pid; +}; + class MultiPaintEvent final : public Event { public: explicit MultiPaintEvent(Vector rects, Gfx::IntSize window_size) diff --git a/Userland/Services/Taskbar/QuickLaunchWidget.cpp b/Userland/Services/Taskbar/QuickLaunchWidget.cpp index 239b7df89a..9d4dcd7155 100644 --- a/Userland/Services/Taskbar/QuickLaunchWidget.cpp +++ b/Userland/Services/Taskbar/QuickLaunchWidget.cpp @@ -256,4 +256,40 @@ void QuickLaunchWidget::drop_event(GUI::DropEvent& event) } } +ErrorOr QuickLaunchWidget::add_from_pid(pid_t pid_to_add) +{ + auto processes_file = TRY(Core::File::open("/sys/kernel/processes"sv, Core::File::OpenMode::Read)); + auto file_content = TRY(processes_file->read_until_eof()); + auto json_obj = TRY(JsonValue::from_string(file_content)).as_object(); + for (auto value : json_obj.get_array("processes"sv).release_value().values()) { + auto& process_object = value.as_object(); + auto pid = process_object.get_i32("pid"sv).value_or(0); + if (pid != pid_to_add) + continue; + + auto executable = process_object.get_deprecated_string("executable"sv); + if (!executable.has_value()) + break; + + auto maybe_name = process_object.get_deprecated_string("name"sv); + if (!maybe_name.has_value()) + break; + + auto name = maybe_name.release_value(); + auto path = executable.release_value(); + if (Desktop::AppFile::exists_for_app(name)) { + path = Desktop::AppFile::app_file_path_for_app(name); + } + + auto new_entry = QuickLaunchEntry::create_from_path(path); + if (!new_entry) + break; + + TRY(add_or_adjust_button(name, new_entry.release_nonnull())); + return true; + } + + return false; +} + } diff --git a/Userland/Services/Taskbar/QuickLaunchWidget.h b/Userland/Services/Taskbar/QuickLaunchWidget.h index 35228425a3..e1b0c88b42 100644 --- a/Userland/Services/Taskbar/QuickLaunchWidget.h +++ b/Userland/Services/Taskbar/QuickLaunchWidget.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, Fabian Blatz + * Copyright (c) 2023, David Ganz * * SPDX-License-Identifier: BSD-2-Clause */ @@ -89,6 +90,8 @@ public: virtual void drag_enter_event(GUI::DragEvent&) override; virtual void drop_event(GUI::DropEvent&) override; + ErrorOr add_from_pid(pid_t pid); + private: explicit QuickLaunchWidget(); ErrorOr add_or_adjust_button(DeprecatedString const&, NonnullOwnPtr&&); diff --git a/Userland/Services/Taskbar/TaskbarWindow.cpp b/Userland/Services/Taskbar/TaskbarWindow.cpp index d31558214c..6d2bf71fc1 100644 --- a/Userland/Services/Taskbar/TaskbarWindow.cpp +++ b/Userland/Services/Taskbar/TaskbarWindow.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2018-2021, Andreas Kling * Copyright (c) 2021, Spencer Dixon + * Copyright (c) 2023, David Ganz * * SPDX-License-Identifier: BSD-2-Clause */ @@ -21,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -360,6 +362,18 @@ void TaskbarWindow::wm_event(GUI::WMEvent& event) workspace_change_event(changed_event.current_row(), changed_event.current_column()); break; } + case GUI::Event::WM_AddToQuickLaunch: { + auto add_event = static_cast(event); + auto result = m_quick_launch->add_from_pid(add_event.pid()); + if (result.is_error()) { + dbgln("Couldn't add pid {} to quick launch menu: {}", add_event.pid(), result.error()); + GUI::MessageBox::show_error(this, String::formatted("Failed to add to Quick Launch: {}", result.release_error()).release_value_but_fixme_should_propagate_errors()); + } else if (!result.release_value()) { + dbgln("Couldn't add pid {} to quick launch menu due to an unexpected error", add_event.pid()); + GUI::MessageBox::show_error(this, "Failed to add to Quick Launch due to an unexpected error."sv); + } + break; + } default: break; } diff --git a/Userland/Services/WindowServer/Window.cpp b/Userland/Services/WindowServer/Window.cpp index be5a79d2ff..ce929de4e2 100644 --- a/Userland/Services/WindowServer/Window.cpp +++ b/Userland/Services/WindowServer/Window.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018-2021, Andreas Kling + * Copyright (c) 2023, David Ganz * * SPDX-License-Identifier: BSD-2-Clause */ @@ -743,6 +744,13 @@ void Window::ensure_window_menu() m_window_menu->add_item(make(*m_window_menu, MenuItem::Type::Separator)); } + auto add_to_quick_launch_item = make(*m_window_menu, (unsigned)WindowMenuAction::AddToQuickLaunch, "&Add to Quick Launch"); + m_window_menu_add_to_quick_launch_item = add_to_quick_launch_item.ptr(); + m_window_menu_add_to_quick_launch_item->set_icon(&pin_icon()); + m_window_menu->add_item(move(add_to_quick_launch_item)); + + m_window_menu->add_item(make(*m_window_menu, MenuItem::Type::Separator)); + auto close_item = make(*m_window_menu, (unsigned)WindowMenuAction::Close, "&Close"); m_window_menu_close_item = close_item.ptr(); m_window_menu_close_item->set_icon(&close_icon()); @@ -792,6 +800,11 @@ void Window::handle_window_menu_action(WindowMenuAction action) WindowManager::the().set_always_on_top(*this, new_is_checked); break; } + case WindowMenuAction::AddToQuickLaunch: { + if (m_process_id.has_value()) + WindowManager::the().on_add_to_quick_launch(m_process_id.value()); + break; + } } } diff --git a/Userland/Services/WindowServer/Window.h b/Userland/Services/WindowServer/Window.h index 13fdb74815..dbf297ad4f 100644 --- a/Userland/Services/WindowServer/Window.h +++ b/Userland/Services/WindowServer/Window.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2018-2023, Andreas Kling + * Copyright (c) 2023, David Ganz * * SPDX-License-Identifier: BSD-2-Clause */ @@ -63,6 +64,7 @@ enum class WindowMenuAction { Close, Move, ToggleAlwaysOnTop, + AddToQuickLaunch, }; enum class WindowMenuDefaultAction { @@ -455,6 +457,7 @@ private: MenuItem* m_window_menu_close_item { nullptr }; MenuItem* m_window_menu_always_on_top_item { nullptr }; MenuItem* m_window_menu_menubar_visibility_item { nullptr }; + MenuItem* m_window_menu_add_to_quick_launch_item { nullptr }; Optional m_progress; bool m_should_show_menubar { true }; WindowStack* m_window_stack { nullptr }; diff --git a/Userland/Services/WindowServer/WindowManager.cpp b/Userland/Services/WindowServer/WindowManager.cpp index f7a0eb3f34..06e9659058 100644 --- a/Userland/Services/WindowServer/WindowManager.cpp +++ b/Userland/Services/WindowServer/WindowManager.cpp @@ -765,6 +765,14 @@ void WindowManager::stop_tile_window_animation() m_tile_window_overlay_animation = nullptr; } +void WindowManager::on_add_to_quick_launch(pid_t pid) +{ + for_each_window_manager([&](WMConnectionFromClient& conn) { + conn.async_add_to_quick_launch(conn.window_id(), pid); + return IterationDecision::Continue; + }); +} + void WindowManager::show_tile_window_overlay(Window& window, Screen const& cursor_screen, WindowTileType tile_type) { m_move_window_suggested_tile = tile_type; diff --git a/Userland/Services/WindowServer/WindowManager.h b/Userland/Services/WindowServer/WindowManager.h index 4aef58bd05..56acd5bb40 100644 --- a/Userland/Services/WindowServer/WindowManager.h +++ b/Userland/Services/WindowServer/WindowManager.h @@ -344,6 +344,8 @@ public: void start_tile_window_animation(Gfx::IntRect const&); void stop_tile_window_animation(); + void on_add_to_quick_launch(pid_t); + private: explicit WindowManager(Gfx::PaletteImpl&); diff --git a/Userland/Services/WindowServer/WindowManagerClient.ipc b/Userland/Services/WindowServer/WindowManagerClient.ipc index 3b31de5a0d..4d0d4588b4 100644 --- a/Userland/Services/WindowServer/WindowManagerClient.ipc +++ b/Userland/Services/WindowServer/WindowManagerClient.ipc @@ -13,4 +13,5 @@ endpoint WindowManagerClient super_digit_key_pressed(i32 wm_id, u8 digit) =| workspace_changed(i32 wm_id, u32 row, u32 column) =| keymap_changed(i32 wm_id, [UTF8] DeprecatedString keymap) =| + add_to_quick_launch(i32 wm_id, i32 process_id) =| }