diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5476f1ac..b8b1c247 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -117,6 +117,7 @@ build-tw: "cmake(Qt6Widgets)" "cmake(Qt6Gui)" "qt6-qml-private-devel" + "qt6-gui-private-devel" "pkgconfig(libcurl)" "pkgconfig(libevent)" "pkgconfig(gstreamer-webrtc-1.0)" diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f7eb942..53501db6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -902,6 +902,10 @@ target_link_libraries(nheko PRIVATE lmdbxx::lmdbxx liblmdb::lmdb) +if(UNIX) + # for wayland activation tokens + target_link_libraries(nheko PRIVATE Qt::GuiPrivate) +endif() if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0") target_precompile_headers(nheko diff --git a/src/main.cpp b/src/main.cpp index e740b27a..3cf794f8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -20,6 +19,10 @@ #include #include +#ifdef Q_OS_UNIX +#include +#endif + #include #include "Cache.h" @@ -248,10 +251,47 @@ main(int argc, char *argv[]) // This check needs to happen _after_ process(), so that we actually print help for --help when // Nheko is already running. if (!singleapp.isPrimaryInstance()) { - std::cout << "Activating main app (instead of opening it a second time)." << std::endl; + auto token = qgetenv("XDG_ACTIVATION_TOKEN"); + +#ifdef Q_OS_UNIX + // getting a valid activation token on wayland is a bit of a pain, it works most reliably + // when you have an actual window, that has the focus... + auto waylandApp = app.nativeInterface(); + if (waylandApp) { + QQuickView window; + window.setTitle("Activate main instance"); + window.setMaximumSize(QSize(100, 50)); + window.setMinimumSize(QSize(100, 50)); + window.setResizeMode(QQuickView::ResizeMode::SizeRootObjectToView); + window.setSource(QUrl(QStringLiteral("qrc:///resources/qml/ui/Spinner.qml"))); + window.show(); + auto waylandWindow = + window.nativeInterface(); + if (waylandWindow) { + std::cout << "Launching temp window to activate main instance!\n"; + QObject::connect( + waylandWindow, + &QNativeInterface::Private::QWaylandWindow::xdgActivationTokenCreated, + waylandWindow, + [&token, &app](QString newToken) { // clazy:exclude=lambda-in-connect + token = newToken.toUtf8(); + app.exit(); + }, + Qt::SingleShotConnection); + QTimer::singleShot(100, waylandWindow, [waylandWindow, waylandApp] { + waylandWindow->requestXdgActivationToken(waylandApp->lastInputSerial()); + }); + app.exec(); + } + } +#endif + + std::cout << "Activating main app (instead of opening it a second time)." + << token.toStdString() << std::endl; + // open uri in main instance // TODO(Nico): Send also an activation token. - singleapp.sendMessage("activate"); + singleapp.sendMessage("activate" + token); if (!matrixUri.isEmpty()) { std::cout << "Sending Matrix URL to main application: " << matrixUri.toStdString() @@ -400,6 +440,11 @@ main(int argc, char *argv[]) ChatPage::instance(), [&](QByteArray message) { if (message.isEmpty() || message.startsWith("activate")) { + auto token = message.remove(0, sizeof("activate") - 1); + if (!token.isEmpty()) { + nhlog::ui()->debug("Setting activation token to: {}", token.toStdString()); + qputenv("XDG_ACTIVATION_TOKEN", token); + } w.show(); w.raise(); w.requestActivate();