11#include < QGuiApplication>
22#include < QApplication>
3+ #include < memory>
34#include < QDateTime>
45#include < QFileInfo>
56#include < QIcon>
67#include < QtQml>
78#include < optional>
89#include < Qt>
910#include < QtWebEngineQuick>
11+ #ifdef USE_WAYLAND_SUBSURFACE
12+ #include < QVulkanInstance>
13+ #endif
1014#include < qtwebenginecoreglobal.h>
1115#include < QtWebEngineCore/QWebEngineProfile>
1216#include < QErrorMessage>
@@ -127,12 +131,6 @@ void ShowLicenseInfo()
127131 printf (" %.*s\n " , static_cast <int >(contents.size ()), contents.data ());
128132}
129133
130- // ///////////////////////////////////////////////////////////////////////////////////////
131- QStringList g_qtFlags = {
132- " --enable-gpu-rasterization" ,
133- " --disable-features=MediaSessionService"
134- };
135-
136134// ///////////////////////////////////////////////////////////////////////////////////////
137135int main (int argc, char *argv[])
138136{
@@ -198,11 +196,39 @@ int main(int argc, char *argv[])
198196 parser.addOption (deleteProfileOption);
199197 parser.addOption (createProfileOption);
200198
199+ #ifdef USE_WAYLAND_SUBSURFACE
200+ // Check if we'll use Wayland Vulkan mode - needs to be early for Qt flags
201+ static bool isWayland = qEnvironmentVariable (" XDG_SESSION_TYPE" ) == " wayland" ||
202+ qEnvironmentVariable (" QT_QPA_PLATFORM" ).contains (" wayland" );
203+ #endif
204+
205+ // Qt flags for WebEngine - skip on Wayland Vulkan to avoid conflicts
206+ QStringList g_qtFlags;
207+ #ifdef USE_WAYLAND_SUBSURFACE
208+ if (!isWayland) {
209+ g_qtFlags << " --enable-gpu-rasterization" << " --disable-features=MediaSessionService" ;
210+ }
211+ #else
212+ g_qtFlags << " --enable-gpu-rasterization" << " --disable-features=MediaSessionService" ;
213+ #endif
214+
201215 char **newArgv = appendCommandLineArguments (argc, argv, g_qtFlags);
202216 int newArgc = argc + g_qtFlags.size ();
203217
204218 preinitQt ();
205219
220+ #ifdef USE_WAYLAND_SUBSURFACE
221+ if (!isWayland) {
222+ detectOpenGLEarly ();
223+ QQuickWindow::setGraphicsApi (QSGRendererInterface::OpenGL);
224+ QCoreApplication::setAttribute (Qt::AA_ShareOpenGLContexts);
225+ }
226+ #else
227+ detectOpenGLEarly ();
228+ QQuickWindow::setGraphicsApi (QSGRendererInterface::OpenGL);
229+ QCoreApplication::setAttribute (Qt::AA_ShareOpenGLContexts);
230+ #endif
231+
206232 QStringList arguments;
207233 for (int i = 0 ; i < argc; i++)
208234 arguments << QString::fromLatin1 (argv[i]);
@@ -230,10 +256,6 @@ int main(int argc, char *argv[])
230256#endif
231257 }
232258
233- detectOpenGLEarly ();
234- QQuickWindow::setGraphicsApi (QSGRendererInterface::OpenGL);
235- QCoreApplication::setAttribute (Qt::AA_ShareOpenGLContexts);
236-
237259 if (parser.isSet (" help" ))
238260 {
239261 // Get Qt's generated help, insert section header before profile options
@@ -423,9 +445,17 @@ int main(int argc, char *argv[])
423445#endif
424446
425447 QStringList chromiumFlags;
448+ #ifdef USE_WAYLAND_SUBSURFACE
449+ if (isWayland) {
450+ // Disable GPU compositing in Chromium - jellyfin-web triggers Vulkan crash with radeon
451+ chromiumFlags << " --disable-gpu-compositing" ;
452+ } else {
453+ #endif
426454#ifdef Q_OS_LINUX
427- // Disable QtWebEngine's automatic MPRIS registration - we handle it ourselves
428- chromiumFlags << " --disable-features=MediaSessionService,HardwareMediaKeyHandling" ;
455+ chromiumFlags << " --disable-features=MediaSessionService,HardwareMediaKeyHandling" ;
456+ #endif
457+ #ifdef USE_WAYLAND_SUBSURFACE
458+ }
429459#endif
430460 // Disable pinch-to-zoom if browser zoom is not allowed
431461 QVariant allowZoom = SettingsComponent::readPreinitValue (SETTINGS_SECTION_MAIN, " allowBrowserZoom" );
@@ -441,27 +471,65 @@ int main(int argc, char *argv[])
441471 if (parser.isSet (" remote-debugging-port" ))
442472 qputenv (" QTWEBENGINE_REMOTE_DEBUGGING" , parser.value (" remote-debugging-port" ).toUtf8 ());
443473
474+ // Must initialize QtWebEngine before QGuiApplication (per Qt docs)
444475 QtWebEngineQuick::initialize ();
476+
477+ #ifdef USE_WAYLAND_SUBSURFACE
478+ // Set Vulkan graphics API for Wayland (must be after QtWebEngineQuick::initialize, before QApplication)
479+ if (isWayland) {
480+ QQuickWindow::setGraphicsApi (QSGRendererInterface::Vulkan);
481+ }
482+ #endif
483+
484+ #ifdef USE_WAYLAND_SUBSURFACE
485+ // Use QGuiApplication for Vulkan (QApplication's widget stuff conflicts with Vulkan)
486+ std::unique_ptr<QCoreApplication> appPtr;
487+ if (isWayland) {
488+ appPtr.reset (new QGuiApplication (newArgc, newArgv));
489+ } else {
490+ appPtr.reset (new QApplication (newArgc, newArgv));
491+ }
492+ auto & app = *appPtr;
493+ #else
445494 QApplication app (newArgc, newArgv);
495+ #endif
496+
497+ #ifdef USE_WAYLAND_SUBSURFACE
498+ // Create Vulkan instance for Qt (must be after QApplication, before QML engine)
499+ static QVulkanInstance vulkanInstance;
500+ if (isWayland) {
501+ vulkanInstance.setApiVersion (QVersionNumber (1 , 2 ));
502+ if (!vulkanInstance.create ()) {
503+ qCritical () << " Failed to create Qt Vulkan instance" ;
504+ return EXIT_FAILURE;
505+ }
506+ }
507+ #endif
446508
447- #if defined(Q_OS_WIN)
509+ #if defined(Q_OS_WIN)
448510 // Setting window icon on OSX will break user ability to change it
449- app. setWindowIcon (QIcon (" :/images/icon.png" ));
511+ qobject_cast<QGuiApplication*>(& app)-> setWindowIcon (QIcon (" :/images/icon.png" ));
450512#endif
451513
452514#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
453515 // Set window icon on Linux using system icon theme
454- app. setWindowIcon (QIcon::fromTheme (" org.jellyfin.JellyfinDesktop" , QIcon (" :/images/icon.png" )));
516+ qobject_cast<QGuiApplication*>(& app)-> setWindowIcon (QIcon::fromTheme (" org.jellyfin.JellyfinDesktop" , QIcon (" :/images/icon.png" )));
455517 // Set app id for Wayland compositor window icon
456- app. setDesktopFileName (" org.jellyfin.JellyfinDesktop" );
518+ qobject_cast<QGuiApplication*>(& app)-> setDesktopFileName (" org.jellyfin.JellyfinDesktop" );
457519#endif
458520
459521 // Configure default WebEngineProfile paths early (profile is already set)
460- {
522+ // Skip early access on Wayland Vulkan - accessing defaultProfile() here triggers
523+ // Chromium GPU init which crashes. Will be set later after QML engine loads.
524+ #ifdef USE_WAYLAND_SUBSURFACE
525+ if (!isWayland) {
526+ #endif
461527 QWebEngineProfile* defaultProfile = QWebEngineProfile::defaultProfile ();
462528 defaultProfile->setCachePath (ProfileManager::activeProfile ().cacheDir (" QtWebEngine" ));
463529 defaultProfile->setPersistentStoragePath (ProfileManager::activeProfile ().dataDir (" QtWebEngine" ));
530+ #ifdef USE_WAYLAND_SUBSURFACE
464531 }
532+ #endif
465533
466534#if defined(Q_OS_MAC) && defined(NDEBUG)
467535 PFMoveToApplicationsFolderIfNecessary ();
@@ -476,7 +544,7 @@ int main(int argc, char *argv[])
476544
477545#ifdef Q_OS_UNIX
478546 // install signals handlers for proper app closing.
479- SignalManager signalManager (&app);
547+ SignalManager signalManager (qobject_cast<QGuiApplication*>( &app) );
480548 Q_UNUSED (signalManager);
481549#endif
482550
@@ -497,7 +565,14 @@ int main(int argc, char *argv[])
497565 SettingsComponent::Get ().setCommandLineValues (parser.optionNames ());
498566
499567 // Set user agent now that SystemComponent is available
500- QWebEngineProfile::defaultProfile ()->setHttpUserAgent (SystemComponent::Get ().getUserAgent ());
568+ // Skip early access on Wayland Vulkan - will be set in objectCreated callback
569+ #ifdef USE_WAYLAND_SUBSURFACE
570+ if (!isWayland) {
571+ #endif
572+ QWebEngineProfile::defaultProfile ()->setHttpUserAgent (SystemComponent::Get ().getUserAgent ());
573+ #ifdef USE_WAYLAND_SUBSURFACE
574+ }
575+ #endif
501576
502577 // load QtWebChannel so that we can register our components with it.
503578 QQmlApplicationEngine *engine = Globals::Engine ();
@@ -517,6 +592,20 @@ int main(int argc, char *argv[])
517592
518593 QQuickWindow* window = Globals::MainWindow ();
519594
595+ #ifdef USE_WAYLAND_SUBSURFACE
596+ // Set Vulkan instance on window when using Vulkan
597+ if (isWayland && vulkanInstance.isValid ()) {
598+ window->setVulkanInstance (&vulkanInstance);
599+ }
600+ // Deferred WebEngineProfile setup for Wayland Vulkan
601+ if (isWayland) {
602+ QWebEngineProfile* defaultProfile = QWebEngineProfile::defaultProfile ();
603+ defaultProfile->setCachePath (ProfileManager::activeProfile ().cacheDir (" QtWebEngine" ));
604+ defaultProfile->setPersistentStoragePath (ProfileManager::activeProfile ().dataDir (" QtWebEngine" ));
605+ defaultProfile->setHttpUserAgent (SystemComponent::Get ().getUserAgent ());
606+ }
607+ #endif
608+
520609 // Set window flags for proper popup handling (e.g., WebEngineView dropdowns)
521610 window->setFlags (window->flags () | Qt::WindowFullscreenButtonHint);
522611
0 commit comments