Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 152 additions & 62 deletions src/gui/main.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/*
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2011 ownCloud GmbH
* SPDX-License-Identifier: GPL-2.0-or-later
*/

#include <QtGlobal>

Check failure on line 7 in src/gui/main.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/main.cpp:7:10 [clang-diagnostic-error]

'QtGlobal' file not found

#include <cmath>
#include <csignal>
Expand Down Expand Up @@ -35,6 +35,29 @@

using namespace OCC;

namespace {
constexpr int InitialTrayWaitSeconds = 1;
constexpr int XfceTrayMaxAttempts = 30;
constexpr int DelayedTrayRetryMs = 10'000;

QByteArray currentDesktopSession()

Check warning on line 43 in src/gui/main.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/main.cpp:43:12 [modernize-use-trailing-return-type]

use a trailing return type for this function
{
const auto xdgCurrentDesktopEnv = qgetenv("XDG_CURRENT_DESKTOP");
const auto desktopSessionEnv = qgetenv("DESKTOP_SESSION");

effective = xdgCurrentDesktopEnv.toLower();
if (effective.isEmpty()) {
effective = desktopSessionEnv.toLower();
}

qCInfo(lcApplication) << "Tray availability check:"
<< "XDG_CURRENT_DESKTOP=" << xdgCurrentDesktopEnv
<< "DESKTOP_SESSION=" << desktopSessionEnv
<< "effective=" << effective;

return effective;
}

void warnSystray()
{
QMessageBox::critical(
Expand All @@ -49,54 +72,175 @@
);
}

enum class RunningInstanceResult {
ContinueStartup = 1, // No running instance detected
ExitHandled = 0, // Existing instance handled or intentionally ignored startup
ExitError = -1, // Existing instance detected, but handoff failed
};

RunningInstanceResult handleRunningInstance(Application &app)

Check warning on line 81 in src/gui/main.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/main.cpp:81:23 [modernize-use-trailing-return-type]

use a trailing return type for this function
{
if (!app.isRunning()) {
qCDebug(lcApplication) << "No running instance detected; continuing startup.";
return RunningInstanceResult::ContinueStartup;
}

qCInfo(lcApplication) << "Another instance is already running.";

if (app.isSessionRestored()) {
qCInfo(lcApplication) << "Session restore detected; not notifying the running instance.";
return RunningInstanceResult::ExitHandled;
}

const QStringList args = app.arguments();

Check warning on line 95 in src/gui/main.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/main.cpp:95:23 [cppcoreguidelines-init-variables]

variable 'args' is not initialized
if (args.size() > 1) {
qCInfo(lcApplication) << "Forwarding startup arguments to the running instance.";
const QString msg = args.join(QLatin1String("|"));

Check warning on line 98 in src/gui/main.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/main.cpp:98:23 [cppcoreguidelines-init-variables]

variable 'msg' is not initialized
if (app.sendMessage(QLatin1String("MSG_PARSEOPTIONS:") + msg)) {
return RunningInstanceResult::ExitHandled;
}

qCWarning(lcApplication) << "Failed to forward startup arguments to the running instance.";
return RunningInstanceResult::ExitError;
}

if (app.backgroundMode()) {
// FIXME: background mode itself is requested via a startup argument... this is unreachable code (!)
qCInfo(lcApplication) << "Background mode requested with no startup arguments; not requesting the running instance to show the main dialog.";
return RunningInstanceResult::ExitHandled;
}

qCInfo(lcApplication) << "Requesting the running instance to show the main dialog.";
// This call is mirrored with the one in Application::slotParseMessage
if (app.sendMessage(QLatin1String("MSG_SHOWMAINDIALOG"))) {
return RunningInstanceResult::ExitHandled;
}

qCWarning(lcApplication) << "Failed to request the main dialog from the running instance.";
return RunningInstanceResult::ExitError;
}

// May block - depending on tray availability
void handleSystemTrayAvailability(Application &app)
{
// Skip this check with appmenu-qt5 because that platform theme hides the system tray (#4693)
if (qgetenv("QT_QPA_PLATFORMTHEME") == "appmenu-qt5") {
return;
}

if (QSystemTrayIcon::isSystemTrayAvailable()) {
return;
}

const auto desktopSession = currentDesktopSession();

// If the system tray is not there, we will wait one second for it to maybe start
// (e.g. boot time) then we show the settings dialog if there is still no system tray.
// On XFCE however, we show a message box with explanation how to install a system tray.

qCInfo(lcApplication) << "System tray is not available, waiting...";
Utility::sleep(InitialTrayWaitSeconds);

Check warning on line 142 in src/gui/main.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/main.cpp:142:20 [cppcoreguidelines-init-variables]

variable 'InitialTrayWaitSeconds' is not initialized

if (desktopSession == "xfce") { // FIXME: This seems too strict; XDG_CURRENT_DESKTOP can be a composite
int attempts = 0;
// NOTE: This loop can block for up to 30 more seconds after the initial sleep (!)
while (!QSystemTrayIcon::isSystemTrayAvailable() && attempts < XfceTrayMaxAttempts) {
++attempts;
Utility::sleep(InitialTrayWaitSeconds);

Check warning on line 149 in src/gui/main.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/main.cpp:149:28 [cppcoreguidelines-init-variables]

variable 'InitialTrayWaitSeconds' is not initialized
}

if (!QSystemTrayIcon::isSystemTrayAvailable()) {
qCWarning(lcApplication) << "System tray unavailable even after waiting 30s (xfce detected)";
warnSystray();
break;
}
}

if (QSystemTrayIcon::isSystemTrayAvailable()) {
app.tryTrayAgain();
return;
}

if (app.backgroundMode() || AccountManager::instance()->accounts().isEmpty()) {
return;
}

if (desktopSession == "ubuntu")) { // FIXME: This seems too strict; XDG_CURRENT_DESKTOP can be a composite (e.g. "ubuntu:gnome")
// Ubuntu desktops may operate acceptably without reporting a traditional tray here.
qCInfo(lcApplication) << "System tray still not available, but assuming it's fine on Ubuntu-like desktop";
return;
}

qCInfo(lcApplication) << "System tray still not available, showing window and trying again later";
app.showMainDialog();
QTimer::singleShot(DelayedTrayRetryMs, &app, &Application::tryTrayAgain);
}
}

// ----------------------------------------------------------------------------------

int main(int argc, char **argv)
{
#ifdef Q_OS_LINUX
const auto appImagePath = qEnvironmentVariable("APPIMAGE");
const auto runningInsideAppImage = !appImagePath.isNull() && QFile::exists(appImagePath);

if (runningInsideAppImage) {
// Work around rendering issues seen when the client runs from an AppImage.
qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--disable-gpu-compositing");
}
#endif

#ifdef Q_OS_WIN
// Avoid loading DLLs from the current working directory.
SetDllDirectory(L"");
// Ensure bundled QML modules are found when launching from the install directory.
// FIXME: confirm the use of currentPath() is intentional here; seems like it
// should be based on the executable path instead (to get install directory)?
qputenv("QML_IMPORT_PATH", (QDir::currentPath() + QStringLiteral("/qml")).toLatin1());
#endif

Q_INIT_RESOURCE(resources);
Q_INIT_RESOURCE(theme);

// OpenSSL 1.1.0: No explicit initialisation or de-initialisation is necessary.
#ifdef Q_OS_MACOS
Mac::CocoaInitializer cocoaInit; // RIIA
#endif

// Prevent context losses / corruptions with some OpenGL drivers (#4340)
auto surfaceFormat = QSurfaceFormat::defaultFormat();
surfaceFormat.setOption(QSurfaceFormat::ResetNotification);
QSurfaceFormat::setDefaultFormat(surfaceFormat);

// Native text rendering is believed to be less blurry (#2409);
// may cause pixelation with advanced features (e.g. text transformation).
// https://doc.qt.io/qt-6/qquickwindow.html#TextRenderType-enum
QQuickWindow::setTextRenderType(QQuickWindow::NativeTextRendering);

// Default styling (i.e. for Linux + UNIX)
auto qmlStyle = QStringLiteral("Fusion");
auto widgetsStyle = QStringLiteral("");

#if defined Q_OS_MACOS
qmlStyle = QStringLiteral("macOS");
widgetsStyle = QStringLiteral("");
#elif defined Q_OS_WIN
if (const auto osVersion = QOperatingSystemVersion::current().version(); osVersion < QOperatingSystemVersion::Windows11.version()) {
const auto osVersion = QOperatingSystemVersion::current().version();
if (osVersion < QOperatingSystemVersion::Windows11.version()) {
// Use the older Qt Quick Controls style on pre-Windows 11 systems.
qmlStyle = QStringLiteral("Universal");
widgetsStyle = QStringLiteral("Fusion");
if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_UNIVERSAL_THEME")) {
// initialise theme with the light/dark mode setting from the OS
// Initialise theme with the light/dark mode setting from the OS.
qputenv("QT_QUICK_CONTROLS_UNIVERSAL_THEME", "System");
}

if (osVersion < QOperatingSystemVersion::Windows10_1809.version() && qEnvironmentVariableIsEmpty("QT_QPA_PLATFORM")) {
// for Windows Server 2016 to display text as expected, see #8064
// Work around DirectWrite-related text rendering issues on older Windows versions like Server 2016 (#8064).
qputenv("QT_QPA_PLATFORM", "windows:nodirectwrite");
}
} else {
// Match newer Windows styling more closely on Windows 11 and later.
qmlStyle = QStringLiteral("FluentWinUI3");
widgetsStyle = QStringLiteral("windows11");
}
Expand Down Expand Up @@ -148,66 +292,12 @@
#endif

// if the application is already running, notify it.
if (app.isRunning()) {
qCInfo(lcApplication) << "Already running, exiting...";
if (app.isSessionRestored()) {
// This call is mirrored with the one in Application::slotParseMessage
qCInfo(lcApplication) << "Session was restored, don't notify app!";
return -1;
}

QStringList args = app.arguments();
if (args.size() > 1) {
QString msg = args.join(QLatin1String("|"));
if (!app.sendMessage(QLatin1String("MSG_PARSEOPTIONS:") + msg))
return -1;
} else if (!app.backgroundMode() && !app.sendMessage(QLatin1String("MSG_SHOWMAINDIALOG"))) {
return -1;
}
return 0;
const auto runningInstanceResult = handleRunningInstance(app);
if (runningInstanceResult != RunningInstanceResult::ContinueStartup) {
return static_cast<int>(runningInstanceResult);
}

// We can't call isSystemTrayAvailable with appmenu-qt5 begause it hides the systemtray
// (issue #4693)
if (qgetenv("QT_QPA_PLATFORMTHEME") != "appmenu-qt5")
{
if (!QSystemTrayIcon::isSystemTrayAvailable()) {
// If the systemtray is not there, we will wait one second for it to maybe start
// (eg boot time) then we show the settings dialog if there is still no systemtray.
// On XFCE however, we show a message box with explainaition how to install a systemtray.
qCInfo(lcApplication) << "System tray is not available, waiting...";
Utility::sleep(1);

auto desktopSession = qgetenv("XDG_CURRENT_DESKTOP").toLower();
if (desktopSession.isEmpty()) {
desktopSession = qgetenv("DESKTOP_SESSION").toLower();
}
if (desktopSession == "xfce") {
int attempts = 0;
while (!QSystemTrayIcon::isSystemTrayAvailable()) {
attempts++;
if (attempts >= 30) {
qCWarning(lcApplication) << "System tray unavailable (xfce)";
warnSystray();
break;
}
Utility::sleep(1);
}
}

if (QSystemTrayIcon::isSystemTrayAvailable()) {
app.tryTrayAgain();
} else if (!app.backgroundMode() && !AccountManager::instance()->accounts().isEmpty()) {
if (desktopSession != "ubuntu") {
qCInfo(lcApplication) << "System tray still not available, showing window and trying again later";
app.showMainDialog();
QTimer::singleShot(10000, &app, &Application::tryTrayAgain);
} else {
qCInfo(lcApplication) << "System tray still not available, but assuming it's fine on 'ubuntu' desktop";
}
}
}
}
handleSystemTrayAvailability(app);

return app.exec();
}
Loading