/* * This file is part of smolbote. It's copyrighted by the contributors recorded * in the version control history of the file, available from its original * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote * * SPDX-License-Identifier: GPL-3.0 */ #include "browser.h" #include "builtins.h" #include "configuration.h" #include "crashhandler.h" #include "session/sessiondialog.h" #include "session_json.hpp" #include "settings.h" #include "util.h" #include "version.h" #include #include #include #include #include #include #include // a helper function to join the keys of a command_map into a string inline std::string join_keys(const command_map &map, const std::string sep = ", ") { std::vector keys(map.size()); std::transform(map.begin(), map.end(), keys.begin(), [](auto pair) { return pair.first; }); std::sort(keys.begin(), keys.end()); std::string k; std::for_each(keys.begin(), keys.end() - 1, [&k, &sep](const std::string &piece) { k += piece + sep; }); k += keys.back(); return k; } int main(int argc, char **argv) { // change log pattern spdlog::set_pattern("[%^%l%$] [%P:%t] %v"); #ifdef QT_DEBUG spdlog::set_level(spdlog::level::debug); // Set global log level to debug #endif const command_map commands{ { "configuration", builtins::configuration }, { "bookmarks", builtins::bookmarks }, }; const std::vector args(argv + 1, argv + argc); args::ArgumentParser parser("smolbote: yet another no-frills browser", "Subcommands: " + join_keys(commands)); parser.Prog(argv[0]); args::HelpFlag cmd_help(parser, "help", "Display this help message.", { 'h', "help" }); args::Flag cmd_version(parser, "version", "Display version information.", { 'v', "version" }); args::Flag cmd_build(parser, "build", "Display build commit.", { 'b', "build" }); args::ValueFlag cmd_config(parser, "config", "Set the configuration file.", { 'c', "config" }); args::Flag cmd_noRemote(parser, "no-remote", "Do not accept or send remote commands.", { "no-remote" }); args::Flag cmd_pickSession(parser, "pick-session", "Show all available sessions and select which one to open", { "pick-session" }); args::ValueFlag cmd_session(parser, "session", "Open the specified session.", { 's', "session" }); args::PositionalList cmd_args(parser, "URL(s)", "List of URLs to open"); cmd_args.KickOut(true); try { auto next = parser.ParseArgs(args); if(cmd_version) return builtins::version(); if(cmd_build) return builtins::build(); // create and load configuration spdlog::debug("Loading configuration {}", init_conf(args::get(cmd_config))); if(cmd_args) { const auto front = args::get(cmd_args).front(); const auto cmd = commands.find(front); if(cmd != commands.end()) { return cmd->second(argv[0], next, std::end(args)); } } } catch(args::Help &) { std::cout << parser; return 0; } catch(args::Error &e) { std::cerr << e.what() << std::endl; std::cerr << parser; return -1; } // argc, argv, allowSecondary Browser app(argc, argv); // set this, otherwise the webview becomes black when using a stylesheet app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); { Configuration conf; if(conf.value("usebreakpad").value()) { CrashHandler::Context ctx( conf.value("path.crashdump").value(), conf.value("path.crashhandler").value()); if(CrashHandler::install_handler(ctx)) { spdlog::info("Installed breakpad crash handler: {}", ctx.dumppath); } else { spdlog::warn("Failed to install breakpad crash handler: {}", ctx.dumppath); } } // load plugins app.loadPlugins(Util::files(conf.value("plugins.path").value(), { "*.so", "*.dll" }), [](const auto *loader) { if(loader->isLoaded()) spdlog::info("Loaded plugin [{}]", qUtf8Printable(loader->fileName())); else spdlog::warn("Loading plugin [{}] failed: {}", qUtf8Printable(loader->fileName()), qUtf8Printable(loader->errorString())); }); } const auto profile = []() { Configuration c; return c.value("profile.default").value(); }(); app.setup(); QStringList urls; for(const auto &u : args::get(cmd_args)) { urls.append(QString::fromStdString(u)); } if(urls.isEmpty()) urls.append(QString()); // if app is primary, create new sessions from received messages if(app.isPrimary() && !cmd_noRemote) { QObject::connect(&app, &Browser::receivedMessage, &app, [&app](quint32 instanceId, QByteArray message) { Q_UNUSED(instanceId); JsonSession session(message); app.open(session.get()); }); } { const auto session = [&]() { if(cmd_session) { QFile sessionJson(QString::fromStdString(args::get(cmd_session))); if(sessionJson.open(QIODevice::ReadOnly | QIODevice::Text)) { return JsonSession(sessionJson.readAll()); } } if(cmd_pickSession) { SessionDialog dlg; if(const auto pick = dlg.pickSession()) { return JsonSession(pick.value()); } } return JsonSession(profile, urls); }(); if(app.isPrimary() || cmd_noRemote) { app.open(session.get()); } else { // app is secondary and not standalone return app.sendMessage(session.serialize()); } } return app.exec(); }