Untitled

 avatar
unknown
plain_text
a month ago
15 kB
2
Indexable
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <getopt.h>
#include <signal.h>
#include <fcntl.h>

#include <unistd.h>
#include <wayfire/debug.hpp>
#include "main.hpp"

#include <wayland-server.h>

#include "wayfire/config-backend.hpp"
#include "core/plugin-loader.hpp"
#include "core/core-impl.hpp"
#include <wlr/render/gbm.h>
#include <wlr/render/renderer.h>

static void print_version()
{
    std::cout << WAYFIRE_VERSION << "-" << wf::version::git_commit <<
        " (" __DATE__ ") branch " << wf::version::git_branch <<
        " wlroots-" << WLR_VERSION_STR << std::endl;
    exit(0);
}

static void print_help()
{
    std::cout << "Wayfire " << WAYFIRE_VERSION << std::endl;
    std::cout << "Usage: wayfire [OPTION]...\n" << std::endl;
    std::cout << " -c,  --config            specify config file to use " <<
        "(overrides WAYFIRE_CONFIG_FILE from the environment)" << std::endl;
    std::cout << " -B,  --config-backend    specify config backend to use" <<
        std::endl;
    std::cout << " -h,  --help              print this help" << std::endl;
    std::cout << " -d,  --debug             enable debug logging" << std::endl;
    std::cout <<
        " -D,  --damage-debug      enable additional debug for damaged regions" <<
        std::endl;
    std::cout << " -R,  --damage-rerender   rerender damaged regions" << std::endl;
    std::cout << " -l,  --legacy-wl-drm     use legacy drm for wayland clients" << std::endl;
    std::cout << " -v,  --version           print version and exit" << std::endl;
    exit(0);
}

static bool drop_permissions(void)
{
    if ((getuid() != geteuid()) || (getgid() != getegid()))
    {
        // Set the gid and uid in the correct order.
        if ((setgid(getgid()) != 0) || (setuid(getuid()) != 0))
        {
            LOGE("Unable to drop root, refusing to start");

            return false;
        }
    }

    if ((setgid(0) != -1) || (setuid(0) != -1))
    {
        LOGE("Unable to drop root (we shouldn't be able to "
             "restore it after setuid), refusing to start");

        return false;
    }

    return true;
}

static wf::log::color_mode_t detect_color_mode()
{
    return isatty(STDOUT_FILENO) ?
           wf::log::LOG_COLOR_MODE_ON : wf::log::LOG_COLOR_MODE_OFF;
}

static void wlr_log_handler(wlr_log_importance level,
    const char *fmt, va_list args)
{
    const int bufsize = 4 * 1024;
    char buffer[bufsize];
    vsnprintf(buffer, bufsize, fmt, args);

    wf::log::log_level_t wlevel;
    switch (level)
    {
      case WLR_ERROR:
        wlevel = wf::log::LOG_LEVEL_ERROR;
        break;

      case WLR_INFO:
        wlevel = wf::log::LOG_LEVEL_INFO;
        break;

      case WLR_DEBUG:
        wlevel = wf::log::LOG_LEVEL_DEBUG;
        break;

      default:
        return;
    }

    bool enabled =
        wf::log::enabled_categories.test((int)wf::log::logging_category::WLR);
    if ((wlevel == wf::log::LOG_LEVEL_DEBUG) && !enabled)
    {
        return;
    }

    wf::log::log_plain(wlevel, buffer);
}

static std::optional<int> exit_because_signal;
static void signal_handler(int signal)
{
    std::string error;
    switch (signal)
    {
      case SIGSEGV:
        error = "Segmentation fault";
        break;

      case SIGFPE:
        error = "Floating-point exception";
        break;

      case SIGABRT:
        error = "Fatal error(SIGABRT)";
        break;

      case SIGINT:
        exit_because_signal = SIGINT;
        wf::get_core().shutdown();
        return;

      case SIGTERM:
        exit_because_signal = SIGTERM;
        wf::get_core().shutdown();
        return;

      default:
        error = "Unknown";
    }

    LOGE("Fatal error: ", error);
    wf::print_trace(false);
    std::_Exit(-1);
}

static std::optional<std::string> choose_socket(wl_display *display)
{
    for (int i = 1; i <= 32; i++)
    {
        auto name = "wayland-" + std::to_string(i);
        if (wl_display_add_socket(display, name.c_str()) >= 0)
        {
            return name;
        }
    }

    return {};
}

static wf::config_backend_t *load_backend(const std::string& backend)
{
    std::string config_plugin(backend);

    if (backend.size())
    {
        std::vector<std::string> plugin_prefixes = wf::get_plugin_paths();
        config_plugin =
            wf::get_plugin_path_for_name(plugin_prefixes, backend).value_or("");
    }

    auto [_, init_ptr] = wf::get_new_instance_handle(config_plugin);

    if (!init_ptr)
    {
        return nullptr;
    }

    using backend_init_t = wf::config_backend_t * (*)();
    auto init = wf::union_cast<void*, backend_init_t>(init_ptr);
    return init();
}

void parse_extended_debugging(const std::vector<std::string>& categories)
{
    for (const auto& cat : categories)
    {
        if (cat == "txn")
        {
            LOGD("Enabling extended debugging for transactions");
            wf::log::enabled_categories.set((size_t)wf::log::logging_category::TXN, 1);
        } else if (cat == "txni")
        {
            LOGD("Enabling extended debugging for transaction objects");
            wf::log::enabled_categories.set((size_t)wf::log::logging_category::TXNI, 1);
        } else if (cat == "views")
        {
            LOGD("Enabling extended debugging for views");
            wf::log::enabled_categories.set((size_t)wf::log::logging_category::VIEWS, 1);
        } else if (cat == "wlroots")
        {
            LOGD("Enabling extended debugging for wlroots");
            wf::log::enabled_categories.set((size_t)wf::log::logging_category::WLR, 1);
        } else if (cat == "scanout")
        {
            LOGD("Enabling extended debugging for direct scanout");
            wf::log::enabled_categories.set((size_t)wf::log::logging_category::SCANOUT, 1);
        } else if (cat == "pointer")
        {
            LOGD("Enabling extended debugging for pointer events");
            wf::log::enabled_categories.set((size_t)wf::log::logging_category::POINTER, 1);
        } else if (cat == "wset")
        {
            LOGD("Enabling extended debugging for workspace set events");
            wf::log::enabled_categories.set((size_t)wf::log::logging_category::WSET, 1);
        } else if (cat == "kbd")
        {
            LOGD("Enabling extended debugging for keyboard events");
            wf::log::enabled_categories.set((size_t)wf::log::logging_category::KBD, 1);
        } else if (cat == "xwayland")
        {
            LOGD("Enabling extended debugging for xwayland events");
            wf::log::enabled_categories.set((size_t)wf::log::logging_category::XWL, 1);
        } else if (cat == "layer-shell")
        {
            LOGD("Enabling extended debugging for layer-shell events");
            wf::log::enabled_categories.set((size_t)wf::log::logging_category::LSHELL, 1);
        } else if (cat == "im")
        {
            LOGD("Enabling extended debugging for input method events");
            wf::log::enabled_categories.set((size_t)wf::log::logging_category::IM, 1);
        } else if (cat == "render")
        {
            LOGD("Enabling extended debugging for render events");
            wf::log::enabled_categories.set((size_t)wf::log::logging_category::RENDER, 1);
        } else
        {
            LOGE("Unrecognized debugging category \"", cat, "\"");
        }
    }
}

// Override assert() handler to be more useful and print a trace.
// extern "C" {
// void __assert_fail(const char* expr, const char *filename, unsigned int line, const char *assert_func)
// {
// LOGE("Assertion failed: ", expr, " at ", filename, ":", line, " in ", assert_func);
// wf::print_trace(false);
// std::_Exit(-1);
// }
// }

// Override exception handling to be more useful and print a trace.
// extern "C" {
// void __cxa_throw(void *ex, void *info, void (*dest)(void*))
// {
// wf::print_trace(false);
// std::_Exit(-1);
// }
// }
//
int main(int argc, char *argv[])
{
    wf::log::log_level_t log_level = wf::log::LOG_LEVEL_INFO;
    struct option opts[] = {
        {
            "config", required_argument, NULL, 'c'
        },
        {
            "config-backend", required_argument, NULL, 'B'
        },
        {"debug", optional_argument, NULL, 'd'},
        {"damage-debug", no_argument, NULL, 'D'},
        {"damage-rerender", no_argument, NULL, 'R'},
        {"legacy-wl-drm", no_argument, NULL, 'l'},
        {"with-great-power-comes-great-responsibility", no_argument, NULL, 'r'},
        {"help", no_argument, NULL, 'h'},
        {"version", no_argument, NULL, 'v'},
        {0, 0, NULL, 0}
    };

    std::string config_file;
    std::string config_backend = WF_DEFAULT_CONFIG_BACKEND;
    std::vector<std::string> extended_debug_categories;
    bool allow_root = false;

    int c, i;
    while ((c = getopt_long(argc, argv, "c:B:d::DhRlrv", opts, &i)) != -1)
    {
        switch (c)
        {
          case 'c':
            config_file = optarg;
            break;

          case 'B':
            config_backend = optarg;
            break;

          case 'D':
            runtime_config.damage_debug = true;
            break;

          case 'R':
            runtime_config.no_damage_track = true;
            break;

          case 'l':
            runtime_config.legacy_wl_drm = true;
            break;

          case 'r':
            allow_root = true;
            break;

          case 'h':
            print_help();
            break;

          case 'd':
            log_level = wf::log::LOG_LEVEL_DEBUG;

            // Make sure to parse things like `-d txn`, which getopt does not.
            // According to documentation, for optional arguments, optarg will
            // be NULL so we need to manually check.
            if (!optarg && (NULL != argv[optind]) &&
                ('-' != argv[optind][0]))
            {
                optarg = argv[optind];
                ++optind;
            }

            if (optarg)
            {
                extended_debug_categories.push_back(optarg);
            }

            break;

          case 'v':
            print_version();
            break;

          default:
            std::cerr << "Unrecognized command line argument " << optarg << "\n" <<
                std::endl;
        }
    }

    /* Don't crash on SIGPIPE, e.g., when doing IPC to a client whose fd has been closed. */
    signal(SIGPIPE, SIG_IGN);

    wf::log::initialize_logging(std::cout, log_level, detect_color_mode());

    parse_extended_debugging(extended_debug_categories);
    wlr_log_init(WLR_DEBUG, wlr_log_handler);

#ifdef PRINT_TRACE
    /* In case of crash, print the stacktrace for debugging.
     * However, if ASAN is enabled, we'll get better stacktrace from there. */
    signal(SIGSEGV, signal_handler);
    signal(SIGFPE, signal_handler);
    signal(SIGABRT, signal_handler);
#endif

    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);

    std::set_terminate([] ()
    {
        std::cout << "Unhandled exception" << std::endl;
        wf::print_trace(false);
        std::abort();
    });

    LOGI("Starting wayfire version ", WAYFIRE_VERSION);
    /* First create display and initialize safe-list's event loop, so that
     * wf objects (which depend on safe-list) can work */
    auto display = wl_display_create();
    auto& core   = wf::compositor_core_impl_t::allocate_core();

    core.argc = argc;
    core.argv = argv;

    /** TODO: move this to core_impl constructor */
    core.display = display;
    core.ev_loop = wl_display_get_event_loop(core.display);
    core.backend = wlr_backend_autocreate(core.ev_loop, &core.session);

 if (getenv("WLR_RDP"))
    {
        wayfire_rdp_init(&core);
    }

    int drm_fd = -1;
    char *drm_device = getenv("WLR_RENDER_DRM_DEVICE");
    if (drm_device)
    {
        drm_fd = open(drm_device, O_RDWR | O_CLOEXEC);
    } else
    {
        drm_fd = wlr_backend_get_drm_fd(core.backend);
    }


/*
    if (drm_fd < 0)
    {
        LOGW("Failed to open DRM render device, consider specifying WLR_RENDER_DRM_DEVICE."
             "Trying SW rendering instead.");
    }

    core.renderer = wlr_gles2_renderer_create_with_drm_fd(drm_fd);
    if (!core.renderer)
    {
        LOGE("Failed to create renderer");
        wl_display_destroy_clients(core.display);
        wl_display_destroy(core.display);
        return EXIT_FAILURE;
    }
*/

// In main.cpp
core.renderer = nullptr;

// Try multiple renderer creation methods
if (drm_fd >= 0) {
    core.renderer = wlr_gles2_renderer_create_with_drm_fd(drm_fd);
}

// Fallback methods
if (!core.renderer) {
    core.renderer = wlr_renderer_autocreate(core.backend);
}

// If still no renderer, try manual GBM renderer creation
if (!core.renderer) {
    struct gbm_device *gbm_dev = gbm_create_device(drm_fd);
    if (gbm_dev) {
        core.renderer = wlr_renderer_create_with_gbm_device(core.backend, gbm_dev);
    }
}

if (!core.renderer)
{
    LOGE("Failed to create renderer");
    wl_display_destroy_clients(core.display);
    wl_display_destroy(core.display);
    return EXIT_FAILURE;
}

    core.allocator = wlr_allocator_autocreate(core.backend, core.renderer);
    assert(core.allocator);
    core.egl = wlr_gles2_renderer_get_egl(core.renderer);
    assert(core.egl);

    if (!allow_root && !drop_permissions())
    {
        wl_display_destroy_clients(core.display);
        wl_display_destroy(core.display);

        return EXIT_FAILURE;
    }

    auto backend = load_backend(config_backend);
    if (!backend)
    {
        LOGE("Failed to load configuration backend!");
        wl_display_destroy_clients(core.display);
        wl_display_destroy(core.display);
        return EXIT_FAILURE;
    }

    LOGD("Using configuration backend: ", config_backend);
    core.config_backend = std::unique_ptr<wf::config_backend_t>(backend);
    core.config_backend->init(display, core.config, config_file);
    core.init();

    auto socket = choose_socket(core.display);
    if (!socket)
    {
        LOGE("Failed to create wayland socket, exiting.");

        return -1;
    }

    core.wayland_display = socket.value();
    LOGI("Using socket name ", core.wayland_display);
    if (!wlr_backend_start(core.backend))
    {
        LOGE("Failed to initialize backend, exiting");
        wlr_backend_destroy(core.backend);
        wl_display_destroy(core.display);

        return -1;
    }

    setenv("WAYLAND_DISPLAY", core.wayland_display.c_str(), 1);
    core.post_init();

    wl_display_run(core.display);
    if (exit_because_signal == SIGINT)
    {
        LOGI("Got SIGINT, shutting down");
    } else if (exit_because_signal == SIGTERM)
    {
        LOGI("Got SIGTERM, shutting down");
    }

    wf::compositor_core_impl_t::deallocate_core();
    LOGI("Shutdown successful!");
    return EXIT_SUCCESS;
}
Leave a Comment