mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 14:46:12 -04:00
229 lines
7.0 KiB
C++
229 lines
7.0 KiB
C++
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
|
|
#include <memory>
|
|
|
|
#include <QDir>
|
|
#include <QFileInfo>
|
|
#include <QFileSystemWatcher>
|
|
#include <QFontDatabase>
|
|
#include <QIcon>
|
|
#include <QApplication>
|
|
#include <QMetaObject>
|
|
#include <QObject>
|
|
#include <QSocketNotifier>
|
|
#include <QTimer>
|
|
#include <QQmlApplicationEngine>
|
|
#include <QQmlContext>
|
|
#include "application_controller.h"
|
|
#include "backend.h"
|
|
#include "qobject.h"
|
|
#include "sharing/linux/app/native_file_logging.h"
|
|
|
|
namespace {
|
|
|
|
constexpr char kQmlHotReloadEnv[] = "NEARBY_QML_HOT_RELOAD";
|
|
constexpr char kQmlSourceDirEnv[] = "NEARBY_QML_SOURCE_DIR";
|
|
constexpr char kDefaultQmlSourceDir[] =
|
|
"/home/lasan/Dev/nearby_latest/sharing/linux/app";
|
|
|
|
int g_signal_pipe[2] = {-1, -1};
|
|
|
|
void HandleTerminationSignal(int signal) {
|
|
const char value = static_cast<char>(signal);
|
|
if (g_signal_pipe[1] != -1) {
|
|
static_cast<void>(write(g_signal_pipe[1], &value, sizeof(value)));
|
|
}
|
|
}
|
|
|
|
bool IsHotReloadEnabled() {
|
|
const QByteArray value = qgetenv(kQmlHotReloadEnv);
|
|
return value == "1" || value.toLower() == "true";
|
|
}
|
|
|
|
QDir QmlSourceDir() {
|
|
const QByteArray configured_dir = qgetenv(kQmlSourceDirEnv);
|
|
if (!configured_dir.isEmpty()) {
|
|
return QDir(QString::fromLocal8Bit(configured_dir));
|
|
}
|
|
return QDir(QString::fromUtf8(kDefaultQmlSourceDir));
|
|
}
|
|
|
|
void WatchQmlFiles(QFileSystemWatcher& watcher, const QDir& source_dir) {
|
|
const QStringList existing_files = watcher.files();
|
|
if (!existing_files.isEmpty()) {
|
|
watcher.removePaths(existing_files);
|
|
}
|
|
|
|
const QStringList files =
|
|
source_dir.entryList(QStringList() << "*.qml", QDir::Files, QDir::Name);
|
|
for (const QString& file : files) {
|
|
watcher.addPath(source_dir.absoluteFilePath(file));
|
|
}
|
|
}
|
|
|
|
void DestroyRootObjects(QQmlApplicationEngine& engine) {
|
|
const QList<QObject*> root_objects = engine.rootObjects();
|
|
for (QObject* object : root_objects) {
|
|
object->deleteLater();
|
|
}
|
|
}
|
|
|
|
bool ReloadInnerContent(QQmlApplicationEngine& engine) {
|
|
const QList<QObject*> root_objects = engine.rootObjects();
|
|
if (root_objects.isEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
QObject* root = root_objects.constFirst();
|
|
return QMetaObject::invokeMethod(root, "reloadInnerContent");
|
|
}
|
|
|
|
bool RequiresFullReload(const QFileInfo& changed_file) {
|
|
return changed_file.fileName() == QStringLiteral("main.qml");
|
|
}
|
|
|
|
void InstallTerminationCleanup(QApplication& app) {
|
|
if (pipe(g_signal_pipe) != 0) {
|
|
return;
|
|
}
|
|
fcntl(g_signal_pipe[0], F_SETFL,
|
|
fcntl(g_signal_pipe[0], F_GETFL, 0) | O_NONBLOCK);
|
|
fcntl(g_signal_pipe[1], F_SETFL,
|
|
fcntl(g_signal_pipe[1], F_GETFL, 0) | O_NONBLOCK);
|
|
|
|
auto* notifier =
|
|
new QSocketNotifier(g_signal_pipe[0], QSocketNotifier::Read, &app);
|
|
QObject::connect(notifier, &QSocketNotifier::activated, &app,
|
|
[&app, notifier](int socket) {
|
|
notifier->setEnabled(false);
|
|
char buffer[32];
|
|
while (read(socket, buffer, sizeof(buffer)) > 0) {
|
|
}
|
|
app.quit();
|
|
});
|
|
|
|
struct sigaction action{};
|
|
action.sa_handler = HandleTerminationSignal;
|
|
sigemptyset(&action.sa_mask);
|
|
action.sa_flags = 0;
|
|
sigaction(SIGINT, &action, nullptr);
|
|
sigaction(SIGTERM, &action, nullptr);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main(int argc, char* argv[]) {
|
|
std::unique_ptr<nearby::sharing::linux::NativeFileLogging> native_logging =
|
|
nearby::sharing::linux::NativeFileLogging::Initialize();
|
|
QApplication app(argc, argv);
|
|
app.setApplicationName(QStringLiteral("QuickShare"));
|
|
app.setOrganizationDomain(QStringLiteral("com.google"));
|
|
app.setDesktopFileName(QStringLiteral("quickshare"));
|
|
app.setWindowIcon(QIcon(QStringLiteral(":/icons/quickshare.svg")));
|
|
app.setQuitOnLastWindowClosed(false);
|
|
|
|
ApplicationController application_controller(app);
|
|
if (!application_controller.ClaimSingleInstance()) {
|
|
return 0;
|
|
}
|
|
|
|
Backend backend;
|
|
application_controller.SetBackend(&backend);
|
|
application_controller.InitializeTray();
|
|
QQmlApplicationEngine engine;
|
|
InstallTerminationCleanup(app);
|
|
|
|
engine.rootContext()->setContextProperty("backend", &backend);
|
|
engine.rootContext()->setContextProperty("appController",
|
|
&application_controller);
|
|
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
|
|
&application_controller,
|
|
[&application_controller](QObject* object, const QUrl&) {
|
|
if (object != nullptr) {
|
|
application_controller.AttachWindow(object);
|
|
}
|
|
});
|
|
|
|
const int fontId = QFontDatabase::addApplicationFont(":/googlesans_var.ttf");
|
|
|
|
if (fontId != -1) {
|
|
const QStringList fontFamilies =
|
|
QFontDatabase::applicationFontFamilies(fontId);
|
|
if (!fontFamilies.isEmpty()) {
|
|
const QFont defaultFont(fontFamilies.at(0));
|
|
app.setFont(defaultFont);
|
|
}
|
|
}
|
|
|
|
if (IsHotReloadEnabled()) {
|
|
const QDir qml_source_dir = QmlSourceDir();
|
|
const QUrl source_url =
|
|
QUrl::fromLocalFile(qml_source_dir.absoluteFilePath("main.qml"));
|
|
QFileSystemWatcher watcher;
|
|
QTimer reload_timer;
|
|
reload_timer.setSingleShot(true);
|
|
reload_timer.setInterval(75);
|
|
QString changed_path;
|
|
|
|
const auto fullReload = [&engine, &watcher, qml_source_dir, source_url]() {
|
|
WatchQmlFiles(watcher, qml_source_dir);
|
|
DestroyRootObjects(engine);
|
|
engine.clearComponentCache();
|
|
engine.load(source_url);
|
|
};
|
|
|
|
const auto reload = [&engine, &watcher, qml_source_dir, source_url,
|
|
&changed_path, &fullReload]() {
|
|
WatchQmlFiles(watcher, qml_source_dir);
|
|
const QFileInfo changed_file(changed_path);
|
|
|
|
if (RequiresFullReload(changed_file) || engine.rootObjects().isEmpty()) {
|
|
fullReload();
|
|
return;
|
|
}
|
|
|
|
engine.clearComponentCache();
|
|
if (!ReloadInnerContent(engine)) {
|
|
fullReload();
|
|
}
|
|
};
|
|
|
|
QObject::connect(&reload_timer, &QTimer::timeout, &app, reload);
|
|
QObject::connect(&watcher, &QFileSystemWatcher::fileChanged, &app,
|
|
[&reload_timer, &changed_path](const QString& path) {
|
|
changed_path = path;
|
|
reload_timer.start();
|
|
});
|
|
QObject::connect(&watcher, &QFileSystemWatcher::directoryChanged, &app,
|
|
[&reload_timer, &changed_path](const QString& path) {
|
|
changed_path = path;
|
|
reload_timer.start();
|
|
});
|
|
|
|
watcher.addPath(qml_source_dir.absolutePath());
|
|
fullReload();
|
|
|
|
const int result = app.exec();
|
|
backend.shutdown();
|
|
return result;
|
|
}
|
|
|
|
// Loaded via the qrc scheme since it's compiled into the binary
|
|
const QUrl url(QStringLiteral("qrc:/main.qml"));
|
|
|
|
QObject::connect(
|
|
&engine, &QQmlApplicationEngine::objectCreated, &app,
|
|
[url](QObject* obj, const QUrl& objUrl) {
|
|
if (!obj && url == objUrl) QCoreApplication::exit(-1);
|
|
},
|
|
Qt::QueuedConnection);
|
|
|
|
engine.load(url);
|
|
|
|
const int result = app.exec();
|
|
backend.shutdown();
|
|
return result;
|
|
}
|