Files
transmission/qt/main.cc
2026-02-13 12:59:17 -06:00

268 lines
6.7 KiB
C++

// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#include <array>
#include <memory>
#include <string_view>
#include <QDir>
#include <QNetworkAccessManager>
#include <fmt/format.h>
#include "lib/base/tr-getopt.h"
#include "lib/transmission/transmission.h"
#include "lib/transmission/utils.h"
#include "lib/transmission/version.h"
#include "lib/app/app.h"
#include "Application.h"
#include "InteropHelper.h"
#include "Prefs.h"
#include "RpcClient.h"
#include "VariantHelpers.h"
using namespace std::string_view_literals;
namespace
{
char const* const DisplayName = "transmission-qt";
auto constexpr FileArgsSeparator = "--"sv;
auto constexpr QtArgsSeparator = "---"sv;
using Arg = tr_option::Arg;
auto constexpr Opts = std::array<tr_option, 8>{ {
{ 'g', "config-dir", "Where to look for configuration files", "g", Arg::Required, "<path>" },
{ 'm', "minimized", "Start minimized in system tray", "m", Arg::None, nullptr },
{ 'p', "port", "Port to use when connecting to an existing session", "p", Arg::Required, "<port>" },
{ 'r', "remote", "Connect to an existing session at the specified hostname", "r", Arg::Required, "<host>" },
{ 'u', "username", "Username to use when connecting to an existing session", "u", Arg::Required, "<username>" },
{ 'v', "version", "Show version number and exit", "v", Arg::None, nullptr },
{ 'w', "password", "Password to use when connecting to an existing session", "w", Arg::Required, "<password>" },
{ 0, nullptr, nullptr, nullptr, Arg::None, nullptr },
} };
static_assert(Opts[std::size(Opts) - 2].val != 0);
} // namespace
namespace
{
char const* getUsage()
{
return "Usage:\n"
" transmission-qt [options...] [[--] torrent files...] [--- Qt options...]";
}
bool tryDelegate(QStringList const& filenames)
{
InteropHelper const interop_client;
if (!interop_client.isConnected())
{
return false;
}
bool delegated = false;
for (auto const& filename : filenames)
{
auto const add_data = AddData(filename);
QString metainfo;
switch (add_data.type)
{
case AddData::URL:
metainfo = add_data.url.toString();
break;
case AddData::MAGNET:
metainfo = add_data.magnet;
break;
case AddData::FILENAME:
case AddData::METAINFO:
metainfo = QString::fromUtf8(add_data.toBase64());
break;
default:
break;
}
if (!metainfo.isEmpty() && interop_client.addMetainfo(metainfo))
{
delegated = true;
}
}
return delegated;
}
} // namespace
int tr_main(int argc, char** argv)
{
tr::app::init();
trqt::variant_helpers::register_qt_converters();
// parse the command-line arguments
bool minimized = false;
QString host;
QString port;
QString username;
QString password;
QString config_dir;
QStringList filenames;
int opt = 0;
char const* arg = nullptr;
int file_args_start_idx = -1;
int qt_args_start_idx = -1;
while (file_args_start_idx < 0 && qt_args_start_idx < 0 &&
(opt = tr_getopt(getUsage(), argc, static_cast<char const* const*>(argv), std::data(Opts), &arg)) != TR_OPT_DONE)
{
switch (opt)
{
case 'g':
config_dir = QString::fromUtf8(arg);
break;
case 'p':
port = QString::fromUtf8(arg);
break;
case 'r':
host = QString::fromUtf8(arg);
break;
case 'u':
username = QString::fromUtf8(arg);
break;
case 'w':
password = QString::fromUtf8(arg);
break;
case 'm':
minimized = true;
break;
case 'v':
fmt::print("{:s} {:s}\n", DisplayName, LONG_VERSION_STRING);
return 0;
case TR_OPT_ERR:
fmt::print(stderr, "Invalid option\n");
tr_getopt_usage(DisplayName, getUsage(), std::data(Opts));
return 1;
default:
if (arg == FileArgsSeparator)
{
file_args_start_idx = tr_optind;
}
else if (arg == QtArgsSeparator)
{
qt_args_start_idx = tr_optind;
}
else
{
filenames.append(QString::fromUtf8(arg));
}
break;
}
}
if (file_args_start_idx >= 0)
{
for (int i = file_args_start_idx; i < argc; ++i)
{
if (argv[i] == QtArgsSeparator)
{
qt_args_start_idx = i + 1;
break;
}
filenames.push_back(QString::fromUtf8(argv[i]));
}
}
InteropHelper::initialize();
// try to delegate the work to an existing copy of Transmission
// before starting ourselves...
if (tryDelegate(filenames))
{
return 0;
}
// set the fallback config dir
if (config_dir.isNull())
{
config_dir = QString::fromStdString(tr_getDefaultConfigDir("transmission"));
}
// initialize the prefs
auto prefs = Prefs{};
prefs.loadFromConfigDir(config_dir);
if (!host.isNull())
{
prefs.set(Prefs::SESSION_REMOTE_HOST, host);
}
if (!port.isNull())
{
prefs.set(Prefs::SESSION_REMOTE_PORT, port.toUInt());
}
if (!username.isNull())
{
prefs.set(Prefs::SESSION_REMOTE_USERNAME, username);
}
if (!password.isNull())
{
prefs.set(Prefs::SESSION_REMOTE_PASSWORD, password);
}
if (!host.isNull() || !port.isNull() || !username.isNull() || !password.isNull())
{
prefs.set(Prefs::SESSION_IS_REMOTE, true);
}
if (prefs.get<bool>(Prefs::START_MINIMIZED))
{
minimized = true;
}
// start as minimized only if the system tray present
if (!prefs.get<bool>(Prefs::SHOW_TRAY_ICON))
{
minimized = false;
}
auto qt_argv = std::vector<char*>{ argv[0] };
if (qt_args_start_idx >= 0)
{
qt_argv.insert(qt_argv.end(), &argv[qt_args_start_idx], &argv[argc]);
}
// run the app
auto qt_argc = static_cast<int>(std::size(qt_argv));
auto nam = QNetworkAccessManager{};
auto rpc = RpcClient{ nam };
auto const app = Application{ prefs, rpc, minimized, config_dir, filenames, qt_argc, std::data(qt_argv) };
auto const ret = QApplication::exec();
// save prefs before exiting
auto const filename = QDir{ config_dir }.absoluteFilePath(QStringLiteral("settings.json"));
prefs.save(filename);
return ret;
}