From 40083444a0f3c2d3a8834eec8696f54eebf10935 Mon Sep 17 00:00:00 2001 From: Nick Vella Date: Wed, 13 Jan 2021 23:10:00 +1100 Subject: [PATCH] Run: initial implementation of Run app --- Base/res/icons/16x16/app-run.png | Bin 0 -> 2704 bytes Base/res/icons/32x32/app-run.png | Bin 0 -> 2782 bytes Userland/Applications/CMakeLists.txt | 1 + Userland/Applications/Run/CMakeLists.txt | 11 ++ Userland/Applications/Run/Run.gml | 65 +++++++++ Userland/Applications/Run/RunWindow.cpp | 170 +++++++++++++++++++++++ Userland/Applications/Run/RunWindow.h | 53 +++++++ Userland/Applications/Run/main.cpp | 54 +++++++ 8 files changed, 354 insertions(+) create mode 100644 Base/res/icons/16x16/app-run.png create mode 100644 Base/res/icons/32x32/app-run.png create mode 100644 Userland/Applications/Run/CMakeLists.txt create mode 100644 Userland/Applications/Run/Run.gml create mode 100644 Userland/Applications/Run/RunWindow.cpp create mode 100644 Userland/Applications/Run/RunWindow.h create mode 100644 Userland/Applications/Run/main.cpp diff --git a/Base/res/icons/16x16/app-run.png b/Base/res/icons/16x16/app-run.png new file mode 100644 index 0000000000000000000000000000000000000000..9cd6ea6ca6a15bc2f070bb78e37acca4df3ff8d1 GIT binary patch literal 2704 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 + * 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 "RunWindow.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +RunWindow::RunWindow() +{ + auto app_icon = GUI::Icon::default_icon("app-run"); + + set_title("Run"); + set_icon(app_icon.bitmap_for_size(16)); + resize(345, 140); + set_resizable(false); + set_minimizable(false); + + auto& main_widget = set_main_widget(); + main_widget.load_from_gml(run_gml); + + m_icon_image_widget = *main_widget.find_descendant_of_type_named("icon"); + m_icon_image_widget->set_bitmap(app_icon.bitmap_for_size(32)); + + m_path_text_box = *main_widget.find_descendant_of_type_named("path"); + m_path_text_box->on_return_pressed = [this] { + m_ok_button->click(); + }; + + m_ok_button = *main_widget.find_descendant_of_type_named("ok_button"); + m_ok_button->on_click = [this](auto) { + do_run(); + }; + + m_cancel_button = *main_widget.find_descendant_of_type_named("cancel_button"); + m_cancel_button->on_click = [this](auto) { + close(); + }; + + m_browse_button = *find_descendant_of_type_named("browse_button"); + m_browse_button->on_click = [this](auto) { + Optional path = GUI::FilePicker::get_open_filepath(this); + if (path.has_value()) + m_path_text_box->set_text(path.value().view()); + }; +} + +RunWindow::~RunWindow() +{ +} + +void RunWindow::event(Core::Event& event) +{ + if (event.type() == GUI::Event::KeyUp || event.type() == GUI::Event::KeyDown) { + auto& key_event = static_cast(event); + if (key_event.key() == Key_Escape) { + // Escape key pressed, close dialog + close(); + return; + } + } + + Window::event(event); +} + +void RunWindow::do_run() +{ + auto run_input = m_path_text_box->text(); + + hide(); + + if (run_via_launch(run_input) || run_as_command(run_input)) { + close(); + return; + } + + GUI::MessageBox::show_error(this, "Failed to run. Please check your command, path, or address, and try again."); + + show(); +} + +bool RunWindow::run_as_command(const String& run_input) +{ + pid_t child_pid; + const char* shell_executable = "/bin/Shell"; // TODO query and use the user's preferred shell. + const char* argv[] = { shell_executable, "-c", run_input.characters(), nullptr }; + + if ((errno = posix_spawn(&child_pid, shell_executable, nullptr, nullptr, const_cast(argv), environ))) { + perror("posix_spawn"); + return false; + } + + // Command spawned in child shell. Hide and wait for exit code. + int status; + if (waitpid(child_pid, &status, 0) < 0) + return false; + + int child_error = WEXITSTATUS(status); + dbgln("Child shell exited with code {}", child_error); + + // 127 is typically the shell indicating command not found. 126 for all other errors. + if (child_error == 126 || child_error == 127) { + return false; + } + + dbgln("Ran via command shell."); + + return true; +} + +bool RunWindow::run_via_launch(const String& run_input) +{ + auto url = URL::create_with_url_or_path(run_input); + + if (url.protocol() == "file") { + auto real_path = Core::File::real_path_for(url.path()); + if (real_path.is_null()) { + // errno *should* be preserved from Core::File::real_path_for(). + warnln("Failed to launch '{}': {}", url.path(), strerror(errno)); + return false; + } + url = URL::create_with_url_or_path(real_path); + } + + if (!Desktop::Launcher::open(url)) { + warnln("Failed to launch '{}'", url); + return false; + } + + dbgln("Ran via URL launch."); + + return true; +} diff --git a/Userland/Applications/Run/RunWindow.h b/Userland/Applications/Run/RunWindow.h new file mode 100644 index 00000000000..f8cd19ea618 --- /dev/null +++ b/Userland/Applications/Run/RunWindow.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021, Nick Vella + * 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 +#include +#include + +class RunWindow final : public GUI::Window { + C_OBJECT(RunWindow) +public: + virtual ~RunWindow() override; + + virtual void event(Core::Event&) override; + +private: + RunWindow(); + + void do_run(); + bool run_as_command(const String& run_input); + bool run_via_launch(const String& run_input); + + RefPtr m_icon_image_widget; + RefPtr m_ok_button; + RefPtr m_cancel_button; + RefPtr m_browse_button; + RefPtr m_path_text_box; +}; diff --git a/Userland/Applications/Run/main.cpp b/Userland/Applications/Run/main.cpp new file mode 100644 index 00000000000..a91608d7461 --- /dev/null +++ b/Userland/Applications/Run/main.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021, Nick Vella + * 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 +#include + +#include "RunWindow.h" + +int main(int argc, char** argv) +{ + if (pledge("stdio sendfd shared_buffer accept cpath rpath unix fattr proc exec", nullptr) < 0) { + perror("pledge"); + return 1; + } + + auto app = GUI::Application::construct(argc, argv); + + if (pledge("stdio sendfd shared_buffer accept rpath unix proc exec", nullptr) < 0) { + perror("pledge"); + return 1; + } + + auto window = RunWindow::construct(); + + window->show(); + + return app->exec(); +}