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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 dF@J;F#d^OTD@}s$vPys&nVznGF6*2UngDc%(m? + * 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 0000000000..f8cd19ea61 --- /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 0000000000..a91608d746 --- /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(); +}