diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,7 +164,7 @@ endif() endif() -find_package(Wayland 1.2 REQUIRED COMPONENTS Cursor OPTIONAL_COMPONENTS Egl) +find_package(Wayland 1.2 REQUIRED COMPONENTS Client Cursor OPTIONAL_COMPONENTS Egl) set_package_properties(Wayland PROPERTIES TYPE REQUIRED PURPOSE "Required for building KWin with Wayland support" @@ -619,6 +619,7 @@ set(kwin_WAYLAND_LIBS KF5::WaylandClient KF5::WaylandServer + Wayland::Client Wayland::Cursor XKB::XKB ${CMAKE_THREAD_LIBS_INIT} diff --git a/main_wayland.cpp b/main_wayland.cpp --- a/main_wayland.cpp +++ b/main_wayland.cpp @@ -277,32 +277,10 @@ static const QString s_waylandPlugin = QStringLiteral("KWinWaylandWaylandBackend"); static const QString s_x11Plugin = QStringLiteral("KWinWaylandX11Backend"); static const QString s_fbdevPlugin = QStringLiteral("KWinWaylandFbdevBackend"); -#if HAVE_DRM static const QString s_drmPlugin = QStringLiteral("KWinWaylandDrmBackend"); -#endif -#if HAVE_LIBHYBRIS static const QString s_hwcomposerPlugin = QStringLiteral("KWinWaylandHwcomposerBackend"); -#endif static const QString s_virtualPlugin = QStringLiteral("KWinWaylandVirtualBackend"); -static QString automaticBackendSelection() -{ - if (qEnvironmentVariableIsSet("WAYLAND_DISPLAY")) { - return s_waylandPlugin; - } - if (qEnvironmentVariableIsSet("DISPLAY")) { - return s_x11Plugin; - } -#if HAVE_LIBHYBRIS - if (qEnvironmentVariableIsSet("ANDROID_ROOT")) { - return s_hwcomposerPlugin; - } -#endif -#if HAVE_DRM - return s_drmPlugin; -#endif - return s_fbdevPlugin; -} static void disablePtrace() { @@ -408,26 +386,44 @@ KWin::Application::createAboutData(); KQuickAddons::QtQuickSettings::init(); - const auto availablePlugins = KPluginLoader::findPlugins(QStringLiteral("org.kde.kwin.waylandbackends")); + // load all plugins metadata + QVector availablePlugins = KPluginLoader::findPlugins(QStringLiteral("org.kde.kwin.waylandbackends")); + // sort plugins descending by autoLoadPriority from JSON metadata + std::sort(availablePlugins.begin(), availablePlugins.end(), + [] (const KPluginMetaData &a, const KPluginMetaData &b) -> bool { + int prio1 = 0, prio2 = 0; + QJsonObject jsonData = a.rawData(); + auto it = jsonData.find(QStringLiteral("autoLoadPriority")); + if (it != jsonData.end()) { + if ((*it).isDouble()) { + prio1 = static_cast((*it).toDouble()); + } + } + jsonData = b.rawData(); + it = jsonData.find(QStringLiteral("autoLoadPriority")); + if (it != jsonData.end()) { + if ((*it).isDouble()) { + prio2 = static_cast((*it).toDouble()); + } + } + return prio2 < prio1; + }); auto hasPlugin = [&availablePlugins] (const QString &name) { return std::any_of(availablePlugins.begin(), availablePlugins.end(), [name] (const KPluginMetaData &plugin) { return plugin.pluginId() == name; } ); }; + const bool hasSizeOption = hasPlugin(KWin::s_x11Plugin) || hasPlugin(KWin::s_virtualPlugin); const bool hasOutputCountOption = hasPlugin(KWin::s_x11Plugin); const bool hasX11Option = hasPlugin(KWin::s_x11Plugin); const bool hasVirtualOption = hasPlugin(KWin::s_virtualPlugin); const bool hasWaylandOption = hasPlugin(KWin::s_waylandPlugin); const bool hasFramebufferOption = hasPlugin(KWin::s_fbdevPlugin); -#if HAVE_DRM const bool hasDrmOption = hasPlugin(KWin::s_drmPlugin); -#endif -#if HAVE_LIBHYBRIS const bool hasHwcomposerOption = hasPlugin(KWin::s_hwcomposerPlugin); -#endif QCommandLineOption xwaylandOption(QStringLiteral("xwayland"), i18n("Start a rootless Xwayland server.")); @@ -490,21 +486,17 @@ if (hasOutputCountOption) { parser.addOption(outputCountOption); } -#if HAVE_LIBHYBRIS QCommandLineOption hwcomposerOption(QStringLiteral("hwcomposer"), i18n("Use libhybris hwcomposer")); if (hasHwcomposerOption) { parser.addOption(hwcomposerOption); } -#endif QCommandLineOption libinputOption(QStringLiteral("libinput"), i18n("Enable libinput support for input events processing. Note: never use in a nested session.")); parser.addOption(libinputOption); -#if HAVE_DRM QCommandLineOption drmOption(QStringLiteral("drm"), i18n("Render through drm node.")); if (hasDrmOption) { parser.addOption(drmOption); } -#endif QCommandLineOption inputMethodOption(QStringLiteral("inputmethod"), i18n("Input method that KWin starts."), @@ -562,12 +554,6 @@ int outputCount = 1; qreal outputScale = 1; -#if HAVE_DRM - if (hasDrmOption && parser.isSet(drmOption)) { - pluginName = KWin::s_drmPlugin; - } -#endif - if (hasSizeOption) { bool ok = false; const int width = parser.value(widthOption).toInt(&ok); @@ -598,6 +584,10 @@ } } + // decide which backend plugin to load based on command-line arguments + if (hasDrmOption && parser.isSet(drmOption)) { + pluginName = KWin::s_drmPlugin; + } if (hasX11Option && parser.isSet(x11DisplayOption)) { deviceIdentifier = parser.value(x11DisplayOption).toUtf8(); pluginName = KWin::s_x11Plugin; @@ -610,18 +600,27 @@ pluginName = KWin::s_fbdevPlugin; deviceIdentifier = parser.value(framebufferDeviceOption).toUtf8(); } -#if HAVE_LIBHYBRIS if (hasHwcomposerOption && parser.isSet(hwcomposerOption)) { pluginName = KWin::s_hwcomposerPlugin; } -#endif if (hasVirtualOption && parser.isSet(virtualFbOption)) { pluginName = KWin::s_virtualPlugin; } if (pluginName.isEmpty()) { - std::cerr << "No backend specified through command line argument, trying auto resolution" << std::endl; - pluginName = KWin::automaticBackendSelection(); + std::cerr << "No backend specified through command line argument, " + "trying auto resolution" << std::endl; + // plugins are sorted by priority in descending order; so, + // take first one that "agrees" to load + for (KPluginMetaData &plugin: availablePlugins) { + KWin::Platform *backendPlugin = qobject_cast(plugin.instantiate()); + if (backendPlugin->canLoad()) { + pluginName = plugin.pluginId(); + std::cerr << "Automatically chosen wayland backend:" << qPrintable(plugin.name()) << std::endl; + break; + } + delete backendPlugin; + } } auto pluginIt = std::find_if(availablePlugins.begin(), availablePlugins.end(), diff --git a/platform.h b/platform.h --- a/platform.h +++ b/platform.h @@ -456,6 +456,16 @@ m_selectedCompositor = type; } + /** + * This method should perform quick preliminary test during plugin + * autodetection phase (when no explicit plugin was specified). + * Test that this plugin has at least a chance to load, with all + * parameters set to default. + * @return true, if it's possible to use this platform plugin + * @since 5.18 + */ + virtual bool canLoad() = 0; + public Q_SLOTS: void pointerMotion(const QPointF &position, quint32 time); void pointerButtonPressed(quint32 button, quint32 time); diff --git a/plugins/platforms/drm/drm.json b/plugins/platforms/drm/drm.json --- a/plugins/platforms/drm/drm.json +++ b/plugins/platforms/drm/drm.json @@ -75,5 +75,6 @@ "Name[zh_CN]": "drm", "Name[zh_TW]": "drm" }, - "input": false + "input": false, + "autoLoadPriority": 40 } diff --git a/plugins/platforms/drm/drm_backend.h b/plugins/platforms/drm/drm_backend.h --- a/plugins/platforms/drm/drm_backend.h +++ b/plugins/platforms/drm/drm_backend.h @@ -121,6 +121,8 @@ return m_devNode; } + bool canLoad() override; + #if HAVE_EGL_STREAMS bool useEglStreams() const { return m_useEglStreams; diff --git a/plugins/platforms/drm/drm_backend.cpp b/plugins/platforms/drm/drm_backend.cpp --- a/plugins/platforms/drm/drm_backend.cpp +++ b/plugins/platforms/drm/drm_backend.cpp @@ -176,6 +176,17 @@ m_dpmsFilter.reset(); } +bool DrmBackend::canLoad() +{ + // basic sanity check like in openDrm() function + UdevDevice::Ptr device = m_udev->primaryGpu(); + if (device) { + return true; + } + // no GPU - no drm backend + return false; +} + void DrmBackend::activate(bool active) { if (active) { diff --git a/plugins/platforms/fbdev/fb_backend.h b/plugins/platforms/fbdev/fb_backend.h --- a/plugins/platforms/fbdev/fb_backend.h +++ b/plugins/platforms/fbdev/fb_backend.h @@ -89,6 +89,8 @@ return QVector{QPainterCompositing}; } + bool canLoad() override; + private: void openFrameBuffer(); bool handleScreenInfo(); diff --git a/plugins/platforms/fbdev/fb_backend.cpp b/plugins/platforms/fbdev/fb_backend.cpp --- a/plugins/platforms/fbdev/fb_backend.cpp +++ b/plugins/platforms/fbdev/fb_backend.cpp @@ -276,4 +276,16 @@ return m_outputs; } +bool FramebufferBackend::canLoad() +{ + // try to open framebuffer almost the same way as in openFrameBuffer() + const QString framebufferDevice = QString(Udev().primaryFramebuffer()->devNode()); + int fd = open(framebufferDevice.toUtf8().constData(), O_RDWR | O_CLOEXEC); + if (fd < 0) { + return false; + } + close(fd); + return true; +} + } diff --git a/plugins/platforms/fbdev/fbdev.json b/plugins/platforms/fbdev/fbdev.json --- a/plugins/platforms/fbdev/fbdev.json +++ b/plugins/platforms/fbdev/fbdev.json @@ -75,5 +75,6 @@ "Name[zh_CN]": "framebuffer", "Name[zh_TW]": "framebuffer" }, - "input": false + "input": false, + "autoLoadPriority": 20 } diff --git a/plugins/platforms/hwcomposer/hwcomposer.json b/plugins/platforms/hwcomposer/hwcomposer.json --- a/plugins/platforms/hwcomposer/hwcomposer.json +++ b/plugins/platforms/hwcomposer/hwcomposer.json @@ -75,5 +75,6 @@ "Name[zh_CN]": "hwcomposer", "Name[zh_TW]": "hwcomposer" }, - "input": false + "input": false, + "autoLoadPriority": 60 } diff --git a/plugins/platforms/hwcomposer/hwcomposer_backend.h b/plugins/platforms/hwcomposer/hwcomposer_backend.h --- a/plugins/platforms/hwcomposer/hwcomposer_backend.h +++ b/plugins/platforms/hwcomposer/hwcomposer_backend.h @@ -101,6 +101,8 @@ return QVector{OpenGLCompositing}; } + bool canLoad() override; + Q_SIGNALS: void outputBlankChanged(); diff --git a/plugins/platforms/hwcomposer/hwcomposer_backend.cpp b/plugins/platforms/hwcomposer/hwcomposer_backend.cpp --- a/plugins/platforms/hwcomposer/hwcomposer_backend.cpp +++ b/plugins/platforms/hwcomposer/hwcomposer_backend.cpp @@ -392,6 +392,27 @@ m_vsyncMutex.unlock(); } +bool HwcomposerBackend::canLoad() +{ + // try to open hwc module & device, like in init() + hw_module_t *hwcModule = nullptr; + if (hw_get_module(HWC_HARDWARE_MODULE_ID, (const hw_module_t **)&hwcModule) != 0) { + qCWarning(KWIN_HWCOMPOSER) << "Failed to get hwcomposer module"; + return false; + } + + hwc_composer_device_1_t *hwcDevice = nullptr; + if (hwc_open_1(hwcModule, &hwcDevice) != 0) { + qCWarning(KWIN_HWCOMPOSER) << "Failed to open hwcomposer device"; + return false; + } + + // close device method in nougat branch: + // https://github.com/ubports/android-headers/blob/xenial/24-caf/hardware/hwcomposer.h#L794 + hwc_close_1(hwcDevice); + return true; +} + static void initLayer(hwc_layer_1_t *layer, const hwc_rect_t &rect, int layerCompositionType) { memset(layer, 0, sizeof(hwc_layer_1_t)); diff --git a/plugins/platforms/virtual/virtual.json b/plugins/platforms/virtual/virtual.json --- a/plugins/platforms/virtual/virtual.json +++ b/plugins/platforms/virtual/virtual.json @@ -75,5 +75,6 @@ "Name[zh_CN]": "virtual", "Name[zh_TW]": "虛擬" }, - "input": true + "input": true, + "autoLoadPriority": 0 } diff --git a/plugins/platforms/virtual/virtual_backend.h b/plugins/platforms/virtual/virtual_backend.h --- a/plugins/platforms/virtual/virtual_backend.h +++ b/plugins/platforms/virtual/virtual_backend.h @@ -64,6 +64,8 @@ return QVector{OpenGLCompositing, QPainterCompositing}; } + bool canLoad() override; + Q_SIGNALS: void virtualOutputsSet(bool countChanged); diff --git a/plugins/platforms/virtual/virtual_backend.cpp b/plugins/platforms/virtual/virtual_backend.cpp --- a/plugins/platforms/virtual/virtual_backend.cpp +++ b/plugins/platforms/virtual/virtual_backend.cpp @@ -112,6 +112,12 @@ return m_enabledOutputs; } +bool VirtualBackend::canLoad() +{ + // our init() never fails + return true; +} + void VirtualBackend::setVirtualOutputs(int count, QVector geometries, QVector scales) { Q_ASSERT(geometries.size() == 0 || geometries.size() == count); diff --git a/plugins/platforms/wayland/wayland.json b/plugins/platforms/wayland/wayland.json --- a/plugins/platforms/wayland/wayland.json +++ b/plugins/platforms/wayland/wayland.json @@ -75,5 +75,6 @@ "Name[zh_CN]": "wayland", "Name[zh_TW]": "wayland" }, - "input": true + "input": true, + "autoLoadPriority": 100 } diff --git a/plugins/platforms/wayland/wayland_backend.h b/plugins/platforms/wayland/wayland_backend.h --- a/plugins/platforms/wayland/wayland_backend.h +++ b/plugins/platforms/wayland/wayland_backend.h @@ -213,6 +213,8 @@ return m_outputs; } + bool canLoad() override; + Q_SIGNALS: void outputAdded(WaylandOutput *output); void outputRemoved(WaylandOutput *output); diff --git a/plugins/platforms/wayland/wayland_backend.cpp b/plugins/platforms/wayland/wayland_backend.cpp --- a/plugins/platforms/wayland/wayland_backend.cpp +++ b/plugins/platforms/wayland/wayland_backend.cpp @@ -65,6 +65,7 @@ #include #include +#include namespace KWin { @@ -833,6 +834,22 @@ return m_outputs; } +bool WaylandBackend::canLoad() +{ + // Try at least to connect to wayland display + // (and immediately disconnect in case of success) + const QByteArray waylandDisplay = qgetenv("WAYLAND_DISPLAY"); + if (waylandDisplay.isNull()) { + return false; + } + struct wl_display *disp = wl_display_connect(waylandDisplay.constData()); + if (!disp) { + return false; + } + wl_display_disconnect(disp); + return true; +} + } } // KWin diff --git a/plugins/platforms/x11/standalone/x11_platform.h b/plugins/platforms/x11/standalone/x11_platform.h --- a/plugins/platforms/x11/standalone/x11_platform.h +++ b/plugins/platforms/x11/standalone/x11_platform.h @@ -78,6 +78,8 @@ Outputs outputs() const override; Outputs enabledOutputs() const override; + bool canLoad() override; + protected: void doHideCursor() override; void doShowCursor() override; diff --git a/plugins/platforms/x11/standalone/x11_platform.cpp b/plugins/platforms/x11/standalone/x11_platform.cpp --- a/plugins/platforms/x11/standalone/x11_platform.cpp +++ b/plugins/platforms/x11/standalone/x11_platform.cpp @@ -558,4 +558,13 @@ return m_outputs; } +bool X11StandalonePlatform::canLoad() +{ + // basically, repeat check from init() function + if (!QX11Info::isPlatformX11()) { + return false; + } + return true; +} + } diff --git a/plugins/platforms/x11/windowed/x11.json b/plugins/platforms/x11/windowed/x11.json --- a/plugins/platforms/x11/windowed/x11.json +++ b/plugins/platforms/x11/windowed/x11.json @@ -76,5 +76,6 @@ "Name[zh_CN]": "x11", "Name[zh_TW]": "x11" }, - "input": true + "input": true, + "autoLoadPriority": 80 } diff --git a/plugins/platforms/x11/windowed/x11windowed_backend.h b/plugins/platforms/x11/windowed/x11windowed_backend.h --- a/plugins/platforms/x11/windowed/x11windowed_backend.h +++ b/plugins/platforms/x11/windowed/x11windowed_backend.h @@ -84,6 +84,8 @@ Outputs outputs() const override; Outputs enabledOutputs() const override; + bool canLoad() override; + Q_SIGNALS: void sizeChanged(); diff --git a/plugins/platforms/x11/windowed/x11windowed_backend.cpp b/plugins/platforms/x11/windowed/x11windowed_backend.cpp --- a/plugins/platforms/x11/windowed/x11windowed_backend.cpp +++ b/plugins/platforms/x11/windowed/x11windowed_backend.cpp @@ -540,4 +540,25 @@ return m_outputs; } +bool X11WindowedBackend::canLoad() +{ + // test X11 connection, almost like in init() + const QByteArray x11display = qgetenv("DISPLAY"); + if (x11display.isEmpty()) { + return false; + } + xcb_connection_t *conn = nullptr; + Display *xDisplay = XOpenDisplay(x11display.constData()); + if (!xDisplay) { + return false; + } + conn = XGetXCBConnection(xDisplay); + if (!conn || xcb_connection_has_error(conn)) { + return false; + } + xcb_disconnect(conn); + XCloseDisplay(xDisplay); + return true; +} + }