diff --git a/.gitmodules b/.gitmodules index 042543cce2..4e7bc273a2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "dependencies/glfw/src"] path = dependencies/glfw/src url = https://github.com/glfw/glfw.git -[submodule "dependencies/zenity/src"] - path = dependencies/zenity/src - url = https://gitea.thebrokenrail.com/minecraft-pi-reborn/zenity.git [submodule "dependencies/LIEF/src"] path = dependencies/LIEF/src url = https://github.com/lief-project/LIEF.git diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index 6fa9e838e1..10ffec48e1 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -8,10 +8,6 @@ endif() if(BUILD_ARM_COMPONENTS AND NOT MCPI_OPEN_SOURCE_ONLY) add_subdirectory(minecraft-pi) endif() -# Zenity (Minimal Build) -if(BUILD_NATIVE_COMPONENTS) - add_subdirectory(zenity) -endif() # LIEF if(BUILD_NATIVE_COMPONENTS OR BUILD_MEDIA_LAYER_CORE) add_subdirectory(LIEF) diff --git a/dependencies/zenity/CMakeLists.txt b/dependencies/zenity/CMakeLists.txt deleted file mode 100644 index 7527448018..0000000000 --- a/dependencies/zenity/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -project(zenity) - -# Silence Warnings -add_compile_options(-w) - -## Zenity - -# Download -set(MESSAGE_QUIET TRUE) -add_subdirectory(src EXCLUDE_FROM_ALL) -unset(MESSAGE_QUIET) - -# Ensure Build -add_custom_target(zenity-build ALL DEPENDS zenity) - -# Install -install(TARGETS zenity DESTINATION "${MCPI_BIN_DIR}") - -# License -install(FILES src/COPYING DESTINATION "${MCPI_LEGAL_DIR}/zenity") diff --git a/dependencies/zenity/src b/dependencies/zenity/src deleted file mode 160000 index a749646116..0000000000 --- a/dependencies/zenity/src +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a7496461161c917878d58131711425e7c8e59436 diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md index b3e18ac472..92bfb4cfbe 100644 --- a/docs/GETTING_STARTED.md +++ b/docs/GETTING_STARTED.md @@ -27,7 +27,7 @@ The AppImage requires Debian Bullseye or higher. This is equivalent to Ubuntu 20 It also requires some additional packages. To install them, run: ```sh -sudo apt install -y libfuse2 libgtk-3-0 libopenal1 libglib2.0-0 +sudo apt install -y libfuse2 libopenal1 libglib2.0-0 ``` diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 889d072ac1..cad5dac2ab 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -9,6 +9,7 @@ add_executable(launcher src/bootstrap/debug.cpp src/util/util.cpp src/util/sdk.cpp + src/util/env.cpp src/logger/logger.cpp src/logger/crash-report.cpp src/options/parser.cpp diff --git a/launcher/src/client/ui.cpp b/launcher/src/client/ui.cpp index 83cff49720..58a54e972f 100644 --- a/launcher/src/client/ui.cpp +++ b/launcher/src/client/ui.cpp @@ -75,28 +75,21 @@ int ConfigurationUI::draw_bottom() { ImGui::SameLine(); } // Right-Align Buttons - const ImGuiStyle &style = ImGui::GetStyle(); - const char *bottom_row_text[] = {"Quit", "Launch"}; - float width_needed = 0; - for (const char *text : bottom_row_text) { - if (width_needed > 0) { - width_needed += style.ItemSpacing.x; + int ret = 0; + draw_right_aligned_buttons({"Quit", "Launch"}, [&ret](const int id, const bool was_clicked) { + if (id == 0) { + // Quit + if (was_clicked) { + ret = -1; + } + ImGui::SetItemTooltip("Changes Will Not Be Saved!"); + } else if (was_clicked) { + // Launch + ret = 1; } - width_needed += ImGui::CalcTextSize(text).x + style.FramePadding.x * 2.f; - } - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - width_needed); - // Quit - if (ImGui::Button(bottom_row_text[0])) { - return -1; - } - ImGui::SetItemTooltip("Changes Will Not Be Saved!"); - ImGui::SameLine(); - // Launch - if (ImGui::Button(bottom_row_text[1])) { - return 1; - } + }); // Return - return 0; + return ret; } // Main Tab diff --git a/launcher/src/logger/crash-report.cpp b/launcher/src/logger/crash-report.cpp index 155de74c24..58ee4fc07d 100644 --- a/launcher/src/logger/crash-report.cpp +++ b/launcher/src/logger/crash-report.cpp @@ -1,35 +1,84 @@ +#include + #include #include "logger.h" +#include "../ui/frame.h" + +// UI +struct CrashReport final : Frame { + explicit CrashReport(const char *filename): Frame("Crash Report", 640, 480) { + // Open File + std::ifstream stream(filename, std::ios_base::binary | std::ios_base::ate); + if (stream) { + // Read File + const std::streamoff size = stream.tellg(); + stream.seekg(0, std::ifstream::beg); + log.resize(size); + stream.read(log.data(), size); + // Close File + stream.close(); + } + } + bool first_render = true; + int render() override { + // Text + ImGui::TextWrapped("%s", MCPI_APP_TITLE " has crashed!"); + ImGui::Spacing(); + ImGui::TextWrapped("Need help? Consider asking on the Discord server!"); + ImGui::Spacing(); + ImGui::TextWrapped("If you believe this is a problem with " MCPI_APP_TITLE " itself, please upload this crash report to the #bugs Discord channel."); + // Log + if (ImGui::BeginChild("Log", ImVec2(0, -ImGui::GetFrameHeightWithSpacing() /* Leave Room For Bottom Row */), ImGuiChildFlags_Borders, ImGuiWindowFlags_HorizontalScrollbar)) { + ImGui::PushFont(monospace); + ImGui::TextUnformatted(log.data(), log.data() + log.size()); + ImGui::PopFont(); + if (first_render) { + ImGui::SetScrollHereY(1.0f); + first_render = false; + } + } + ImGui::EndChild(); + // Buttons + if (ImGui::Button("Join Discord")) { + open_url(MCPI_DISCORD_INVITE); + } + ImGui::SameLine(); + if (ImGui::Button("View All Logs")) { + open_url("file://" + get_logs_folder()); + } + ImGui::SameLine(); + // Right-Aligned + int ret = 0; + const std::string &log_ref = log; + draw_right_aligned_buttons({"Copy", "Quit"}, [&ret, &log_ref](const int id, const bool was_clicked) { + if (was_clicked) { + if (id == 0) { + // Copy Log + ImGui::SetClipboardText(log_ref.c_str()); + } else { + // Exit + ret = 1; + } + } + }); + return ret; + } + std::string log; +}; // Show Crash Report Dialog -#define DIALOG_TITLE "Crash Report" -#define CRASH_REPORT_DIALOG_WIDTH "640" -#define CRASH_REPORT_DIALOG_HEIGHT "480" void show_report(const char *log_filename) { // Fork - pid_t pid = fork(); + const pid_t pid = fork(); if (pid == 0) { // Child setsid(); ALLOC_CHECK(freopen("/dev/null", "w", stdout)); ALLOC_CHECK(freopen("/dev/null", "w", stderr)); ALLOC_CHECK(freopen("/dev/null", "r", stdin)); - const char *command[] = { - "zenity", - "--title", DIALOG_TITLE, - "--name", MCPI_APP_ID, - "--width", CRASH_REPORT_DIALOG_WIDTH, - "--height", CRASH_REPORT_DIALOG_HEIGHT, - "--text-info", - "--text", MCPI_APP_TITLE " has crashed!\n\nNeed help? Consider asking on the Discord server! If you believe this is a problem with " MCPI_APP_TITLE " itself, please upload this crash report to the #bugs Discord channel.", - "--filename", log_filename, - "--no-wrap", - "--font", "Monospace", - "--save-filename", MCPI_VARIANT_NAME "-crash-report.log", - "--ok-label", "Exit", - nullptr - }; - safe_execvpe(command, (const char *const *) environ); + CrashReport ui(log_filename); + ui.run(); + exit(EXIT_SUCCESS); } } \ No newline at end of file diff --git a/launcher/src/logger/logger.cpp b/launcher/src/logger/logger.cpp index a100c24c89..a7e2ed2e37 100644 --- a/launcher/src/logger/logger.cpp +++ b/launcher/src/logger/logger.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -26,12 +25,18 @@ static void exit_handler(__attribute__((unused)) int signal) { // Log File static std::string log_filename; static int log_fd; -static void setup_log_file() { - // Get Log Directory +std::string get_logs_folder() { const std::string home = std::string(getenv(_MCPI_HOME_ENV)) + get_home_subdirectory_for_game_data(); ensure_directory(home.c_str()); const std::string logs = home + "/logs"; ensure_directory(logs.c_str()); + return logs; +} +static void setup_log_file() { + // Get Log Directory + const std::string home = std::string(getenv(_MCPI_HOME_ENV)) + get_home_subdirectory_for_game_data(); + ensure_directory(home.c_str()); + const std::string logs = get_logs_folder(); // Get Timestamp time_t raw_time; @@ -129,7 +134,7 @@ void setup_logger() { // Close Log File close(log_fd); - unsetenv(_MCPI_LOG_FD_ENV); + reborn_set_log(-1); // Show Crash Log if (is_crash && !reborn_is_headless()) { diff --git a/launcher/src/logger/logger.h b/launcher/src/logger/logger.h index 5989ac0e13..01f34b5c8e 100644 --- a/launcher/src/logger/logger.h +++ b/launcher/src/logger/logger.h @@ -1,4 +1,7 @@ #pragma once +#include + +std::string get_logs_folder(); void setup_logger(); void show_report(const char *log_filename); \ No newline at end of file diff --git a/launcher/src/main.cpp b/launcher/src/main.cpp index e20d4f0625..fdd60cf978 100644 --- a/launcher/src/main.cpp +++ b/launcher/src/main.cpp @@ -24,47 +24,11 @@ static void setup_environment(const options_t &options) { bind_to_env(_MCPI_FORCE_HEADLESS_ENV, options.force_headless); bind_to_env(_MCPI_FORCE_NON_HEADLESS_ENV, options.force_non_headless); - // GTK Dark Mode - set_and_print_env("GTK_THEME", "Adwaita:dark"); - // Configure PATH - { - // Get Binary Directory - const std::string binary_directory = get_binary_directory(); - std::string new_path = binary_directory + "/bin"; - // Add Existing PATH - { - const char *value = getenv("PATH"); - if (value != nullptr && strlen(value) > 0) { - new_path += std::string(":") + value; - } - } - // Set And Free - set_and_print_env("PATH", new_path.c_str()); - } + setup_path(); // Setup MCPI_HOME - const char *custom_profile_directory = getenv(MCPI_PROFILE_DIRECTORY_ENV); - if (custom_profile_directory != nullptr) { - // Custom Directory - custom_profile_directory = realpath(custom_profile_directory, nullptr); - ALLOC_CHECK(custom_profile_directory); - set_and_print_env(_MCPI_HOME_ENV, custom_profile_directory); - free((void *) custom_profile_directory); - } else if (!reborn_is_server()) { - // Ensure $HOME - const char *home = getenv("HOME"); - if (home == nullptr) { - ERR("$HOME Is Not Set"); - } - set_and_print_env(_MCPI_HOME_ENV, home); - } else { - // Set Home To Current Directory, So World Data Is Stored There - char *launch_directory = getcwd(nullptr, 0); - ALLOC_CHECK(launch_directory); - set_and_print_env(_MCPI_HOME_ENV, launch_directory); - free(launch_directory); - } + setup_home(); // Create If Needed const std::string minecraft_folder = std::string(getenv(_MCPI_HOME_ENV)) + get_home_subdirectory_for_game_data(); ensure_directory(minecraft_folder.c_str()); diff --git a/launcher/src/ui/frame.cpp b/launcher/src/ui/frame.cpp index 72d43f28b5..5fb4bc34f0 100644 --- a/launcher/src/ui/frame.cpp +++ b/launcher/src/ui/frame.cpp @@ -12,8 +12,9 @@ Frame::Frame(const char *title, const int width, const int height) { // Create Window init_glfw(); window = create_glfw_window(title, width, height); - // V-Sync - glfwSwapInterval(1); + // Disable V-Sync + // (On Wayland, This Fixes Issues With The Clipboard) + glfwSwapInterval(0); // Setup ImGui Context IMGUI_CHECKVERSION(); ImGui::CreateContext(); @@ -26,9 +27,11 @@ Frame::Frame(const char *title, const int width, const int height) { ImGui_ImplOpenGL2_Init(); } Frame::~Frame() { + // Shutdown ImGui ImGui_ImplOpenGL2_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); + // Cleanup GLFW cleanup_glfw(window); } @@ -85,3 +88,24 @@ void Frame::setup_style(const float scale) { style.ScaleAllSizes(scale); patch_colors(style); } + +// Right-Aligned Buttons +void Frame::draw_right_aligned_buttons(const std::vector &buttons, const std::function &callback) { + // Calculate Position + const ImGuiStyle &style = ImGui::GetStyle(); + float width_needed = 0; + for (const char *text : buttons) { + if (width_needed > 0) { + width_needed += style.ItemSpacing.x; + } + width_needed += ImGui::CalcTextSize(text).x + style.FramePadding.x * 2.0f; + } + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - width_needed); + // Draw + for (std::vector::size_type id = 0; id < buttons.size(); id++) { + if (id > 0) { + ImGui::SameLine(); + } + callback(int(id), ImGui::Button(buttons[id])); + } +} \ No newline at end of file diff --git a/launcher/src/ui/frame.h b/launcher/src/ui/frame.h index 1d5fbd0f1a..372459f63b 100644 --- a/launcher/src/ui/frame.h +++ b/launcher/src/ui/frame.h @@ -1,22 +1,29 @@ #pragma once +#include +#include + #include -#include #include // UI Frame struct Frame { Frame(const char *title, int width, int height); virtual ~Frame(); + // Prevent Copying + Frame(const Frame &) = delete; + Frame &operator=(const Frame &) = delete; // Run int run(); virtual int render() = 0; - // Properties protected: + // API For Sub-Classes ImFont *monospace = nullptr; + static void draw_right_aligned_buttons(const std::vector &buttons, const std::function &callback); private: + // Properties GLFWwindow *window = nullptr; - // Internal + // Internal Methods float get_scale(); void setup_style(float scale); static void patch_colors(ImGuiStyle &style); diff --git a/launcher/src/util/env.cpp b/launcher/src/util/env.cpp new file mode 100644 index 0000000000..e2f2eb3797 --- /dev/null +++ b/launcher/src/util/env.cpp @@ -0,0 +1,46 @@ +#include "util.h" + +#include + +// $PATH +void setup_path() { + // Get Binary Directory + const std::string binary_directory = get_binary_directory(); + std::string new_path = binary_directory + "/bin"; + // Add Existing PATH + const char *value = getenv("PATH"); + if (value != nullptr && strlen(value) > 0) { + new_path += std::string(":") + value; + } + // Set And Free + set_and_print_env("PATH", new_path.c_str()); +} + +// Profile Directory +void setup_home() { + const char *custom_profile_directory = getenv(MCPI_PROFILE_DIRECTORY_ENV); + std::string home; + if (custom_profile_directory != nullptr) { + // Custom Directory + home = safe_realpath(custom_profile_directory); + } else if (!reborn_is_server()) { + // Ensure $HOME + const char *value = getenv("HOME"); + if (value == nullptr) { + ERR("$HOME Is Not Set"); + } + home = value; + // Flatpak +#ifdef MCPI_IS_FLATPAK_BUILD + home += "/.var/app/" MCPI_APP_ID; +#endif + } else { + // Set Home To Current Directory, So World Data Is Stored There + char *launch_directory = getcwd(nullptr, 0); + ALLOC_CHECK(launch_directory); + home = launch_directory; + free(launch_directory); + } + // Set + set_and_print_env(_MCPI_HOME_ENV, home.c_str()); +} \ No newline at end of file diff --git a/launcher/src/util/util.h b/launcher/src/util/util.h index 4bffa54b9f..27b26621d7 100644 --- a/launcher/src/util/util.h +++ b/launcher/src/util/util.h @@ -8,4 +8,7 @@ void chop_last_component(std::string &str); std::string safe_realpath(const std::string &path); std::string get_binary_directory(); -void copy_sdk(const std::string &binary_directory, bool log_with_debug); \ No newline at end of file +void copy_sdk(const std::string &binary_directory, bool log_with_debug); + +void setup_path(); +void setup_home(); \ No newline at end of file diff --git a/libreborn/include/libreborn/exec.h b/libreborn/include/libreborn/exec.h index d7b0bed74b..64a6ad1939 100644 --- a/libreborn/include/libreborn/exec.h +++ b/libreborn/include/libreborn/exec.h @@ -28,4 +28,7 @@ std::vector *run_command(const char *const command[], int *exit_s bool is_exit_status_success(int status); // Get Exit Status String -std::string get_exit_status_string(int status); \ No newline at end of file +std::string get_exit_status_string(int status); + +// Open URL +void open_url(const std::string &url); \ No newline at end of file diff --git a/libreborn/src/util/exec.cpp b/libreborn/src/util/exec.cpp index 3e5d8391e9..6bba724d82 100644 --- a/libreborn/src/util/exec.cpp +++ b/libreborn/src/util/exec.cpp @@ -101,6 +101,10 @@ void poll_fds(const std::vector &fds, const std::function *output = run_command(command, &return_code); + delete output; + if (!is_exit_status_success(return_code)) { + WARN("Unable To Open URL: %s", url.c_str()); + } +} \ No newline at end of file diff --git a/libreborn/src/util/log.cpp b/libreborn/src/util/log.cpp index 2235a08e28..36a41df594 100644 --- a/libreborn/src/util/log.cpp +++ b/libreborn/src/util/log.cpp @@ -39,7 +39,7 @@ int reborn_get_log_fd() { void reborn_set_log(const int fd) { // Set Variable log_fd = -1; - set_and_print_env(_MCPI_LOG_FD_ENV, std::to_string(fd).c_str()); + set_and_print_env(_MCPI_LOG_FD_ENV, fd >= 0 ? std::to_string(fd).c_str() : nullptr); } // Debug Logging diff --git a/mods/include/mods/options/info.h b/mods/include/mods/options/info.h index d92a6d8c90..4ec72ab4f8 100644 --- a/mods/include/mods/options/info.h +++ b/mods/include/mods/options/info.h @@ -2,8 +2,4 @@ #include -#define CHANGELOG_FILE "CHANGELOG.md" - -extern "C" { -void open_url(const std::string &url); -} \ No newline at end of file +#define CHANGELOG_FILE "CHANGELOG.md" \ No newline at end of file diff --git a/mods/src/misc/misc.cpp b/mods/src/misc/misc.cpp index 062e19dc11..ec5badcfbc 100644 --- a/mods/src/misc/misc.cpp +++ b/mods/src/misc/misc.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include @@ -209,7 +208,7 @@ static AppPlatform_readAssetFile_return_value AppPlatform_readAssetFile_injectio return ret; } // Read File - std::streamoff len = stream.tellg(); + const std::streamoff len = stream.tellg(); char *buf = new char[len]; ALLOC_CHECK(buf); stream.seekg(0, std::ifstream::beg); diff --git a/mods/src/options/info.cpp b/mods/src/options/info.cpp index 04be1e8efb..da82b8e4de 100644 --- a/mods/src/options/info.cpp +++ b/mods/src/options/info.cpp @@ -38,29 +38,6 @@ static std::string extra_version_info = ; static std::string extra_version_info_full = !extra_version_info.empty() ? (" (" + extra_version_info + ")") : ""; -// Profile Directory -static std::string profile_directory_suffix = -#ifdef MCPI_IS_FLATPAK_BUILD - "/.var/app/" MCPI_APP_ID + -#endif - std::string(get_home_subdirectory_for_game_data()) - ; -static std::string get_profile_directory_url() { - std::string directory; - if (getenv(MCPI_PROFILE_DIRECTORY_ENV) != nullptr) { - // Using Custom Directory - directory = home_get(); - } else { - // Determine Proper Directory - const char *home = getenv("HOME"); - if (home == nullptr) { - IMPOSSIBLE(); - } - directory = home + profile_directory_suffix; - } - return std::string("file://") + directory; -} - // Info Data struct info_line { std::string (*get_text)(); @@ -80,7 +57,7 @@ static info_line info[] = { .get_text = []() { return std::string("Profile Directory"); }, - .button_url = get_profile_directory_url(), + .button_url = std::string("file://") + home_get(), .button_text = "Open" }, { @@ -157,17 +134,6 @@ static void position_info(Font *font, const int width, const int height) { } } -// Open URL -void open_url(const std::string &url) { - int return_code; - const char *command[] = {"xdg-open", url.c_str(), nullptr}; - const std::vector *output = run_command(command, &return_code); - delete output; - if (!is_exit_status_success(return_code)) { - WARN("Unable To Open URL: %s", url.c_str()); - } -} - // Create VTable struct InfoScreen final : CustomScreen { // Buttons diff --git a/scripts/install-dependencies.sh b/scripts/install-dependencies.sh index 74b12959eb..8c23e87a29 100755 --- a/scripts/install-dependencies.sh +++ b/scripts/install-dependencies.sh @@ -59,8 +59,7 @@ run_build() { "libxinerama-dev:$1" \ "libxrandr-dev:$1" \ "libxext-dev:$1" \ - `# Zenity Dependencies` \ - "libgtk-3-dev:$1" \ + `# QEMU Dependencies` \ "libglib2.0-dev:$1" \ `# AppStream Verification` \ appstream