diff --git a/src/compositor/CMakeLists.txt b/src/compositor/CMakeLists.txt index 373f951f..7afd4827 100644 --- a/src/compositor/CMakeLists.txt +++ b/src/compositor/CMakeLists.txt @@ -18,7 +18,6 @@ install(FILES ${LiriShell_QM_FILES} DESTINATION "${KDE_INSTALL_DATADIR}/liri-shell/translations") set(SOURCES - application.cpp application.h main.cpp sessionmanager/sessionmanager.cpp sessionmanager/sessionmanager.h ${LiriShell_QM_FILES} @@ -29,7 +28,7 @@ file(GLOB_RECURSE _qml_files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" CMAKE_CONFIG file(GLOB_RECURSE _js_files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" CMAKE_CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/qml/*.js") file(GLOB_RECURSE _image_files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" CMAKE_CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/images/*") qt6_add_qml_module(LiriShell - URI compositor + URI LiriShell QML_FILES ${_qml_files} ${_js_files} RESOURCES ${_image_files} ) diff --git a/src/compositor/application.cpp b/src/compositor/application.cpp deleted file mode 100644 index 1f5b0af7..00000000 --- a/src/compositor/application.cpp +++ /dev/null @@ -1,181 +0,0 @@ -// SPDX-FileCopyrightText: 2018 Pier Luigi Fiorini -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "application.h" -#include "sessionmanager/sessionmanager.h" - -#include -#include - -static const QEvent::Type StartupEventType = static_cast(QEvent::registerEventType()); - -static int convertPermission(const QFileInfo &fileInfo) -{ - int p = 0; - - if (fileInfo.permission(QFile::ReadUser)) - p += 400; - if (fileInfo.permission(QFile::WriteUser)) - p += 200; - if (fileInfo.permission(QFile::ExeUser)) - p += 100; - if (fileInfo.permission(QFile::ReadGroup)) - p += 40; - if (fileInfo.permission(QFile::WriteGroup)) - p += 20; - if (fileInfo.permission(QFile::ExeGroup)) - p += 10; - if (fileInfo.permission(QFile::ReadOther)) - p += 4; - if (fileInfo.permission(QFile::WriteOther)) - p += 2; - if (fileInfo.permission(QFile::ExeOther)) - p += 1; - - return p; -} - -Application::Application(QObject *parent) - : QObject(parent) - , m_failSafe(false) - , m_started(false) -{ - // Application engine - m_appEngine = new QQmlApplicationEngine(this); - connect(m_appEngine, &QQmlApplicationEngine::objectCreated, - this, &Application::objectCreated); - - // Session manager - m_sessionManager = new SessionManager(this); - - // Invoke shutdown sequence when quitting - connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, - this, &Application::shutdown); -} - -QString Application::screenConfigurationFileName() const -{ - return m_screenConfigFileName; -} - -void Application::setScreenConfigurationFileName(const QString &fileName) -{ - m_screenConfigFileName = fileName; -} - -void Application::setUrl(const QUrl &url) -{ - m_url = url; -} - -void Application::customEvent(QEvent *event) -{ - if (event->type() == StartupEventType) - startup(); -} - -void Application::verifyXdgRuntimeDir() -{ - QString dirName = QString::fromLocal8Bit(qgetenv("XDG_RUNTIME_DIR")); - - if (dirName.isEmpty()) { - QString msg = QObject::tr( - "The XDG_RUNTIME_DIR environment variable is not set.\n" - "Refer to your distribution on how to get it, or read\n" - "http://www.freedesktop.org/wiki/Specifications/basedir-spec\n" - "on how to implement it.\n"); - qFatal("%s", qPrintable(msg)); - } - - QFileInfo fileInfo(dirName); - - if (!fileInfo.exists()) { - QString msg = QObject::tr( - "The XDG_RUNTIME_DIR environment variable is set to " - "\"%1\", which doesn't exist.\n").arg(dirName.constData()); - qFatal("%s", qPrintable(msg)); - } - - if (convertPermission(fileInfo) != 700 || fileInfo.ownerId() != getuid()) { - QString msg = QObject::tr( - "XDG_RUNTIME_DIR is set to \"%1\" and is not configured correctly.\n" - "Unix access mode must be 0700, but is 0%2.\n" - "It must also be owned by the current user (UID %3), " - "but is owned by UID %4 (\"%5\").\n") - .arg(dirName.constData()) - .arg(convertPermission(fileInfo)) - .arg(getuid()) - .arg(fileInfo.ownerId()) - .arg(fileInfo.owner()); - qFatal("%s\n", qPrintable(msg)); - } -} - -void Application::startup() -{ - // Can't do the startup sequence twice - if (m_started) - return; - - // Check whether XDG_RUNTIME_DIR is ok or not - verifyXdgRuntimeDir(); - - // Set screen configuration file name - m_appEngine->rootContext()->setContextProperty(QStringLiteral("screenConfigurationFileName"), - m_screenConfigFileName); - - // Session interface - m_appEngine->rootContext()->setContextProperty(QStringLiteral("SessionInterface"), - m_sessionManager); - - // Load the compositor - m_appEngine->load(m_url); - - m_started = true; -} - -void Application::shutdown() -{ - m_appEngine->deleteLater(); - m_appEngine = nullptr; - - m_sessionManager->deleteLater(); - m_sessionManager = nullptr; -} - -void Application::objectCreated(QObject *object, const QUrl &) -{ - // All went fine - if (object) - return; - - // Compositor failed to load - if (m_failSafe) { - // We give up because even the error screen has an error - qWarning("A fatal error has occurred while running Liri Shell, but the error " - "screen has errors too. Giving up."); - QCoreApplication::exit(1); - } else { - // Load the error screen in case of error - m_failSafe = true; - m_appEngine->load(QUrl(QStringLiteral("qrc:/qt/qml/compositor/qml/error/ErrorCompositor.qml"))); - } -} - -StartupEvent::StartupEvent() - : QEvent(StartupEventType) -{ -} - -#include "moc_application.cpp" diff --git a/src/compositor/application.h b/src/compositor/application.h deleted file mode 100644 index c3d135f6..00000000 --- a/src/compositor/application.h +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-FileCopyrightText: 2018 Pier Luigi Fiorini -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include -#include -#include - -QT_FORWARD_DECLARE_CLASS(QQmlApplicationEngine) - -class ScreenSaver; -class SessionManager; - -class Application : public QObject -{ - Q_OBJECT -public: - explicit Application(QObject *parent = nullptr); - - QString screenConfigurationFileName() const; - void setScreenConfigurationFileName(const QString &fileName); - - void setUrl(const QUrl &url); - -protected: - void customEvent(QEvent *event) override; - -private: - QUrl m_url; - QQmlApplicationEngine *m_appEngine; - SessionManager *m_sessionManager; - bool m_failSafe; - bool m_started; - QString m_screenConfigFileName; - - void verifyXdgRuntimeDir(); - -private Q_SLOTS: - void startup(); - void shutdown(); - void objectCreated(QObject *object, const QUrl &); -}; - -class StartupEvent : public QEvent -{ -public: - StartupEvent(); -}; diff --git a/src/compositor/main.cpp b/src/compositor/main.cpp index 2bad50d3..9650753f 100644 --- a/src/compositor/main.cpp +++ b/src/compositor/main.cpp @@ -8,14 +8,13 @@ #include #include #include +#include #include #include #include #include -#include "application.h" - #if HAVE_SYS_PRCTL_H #include #endif @@ -24,8 +23,12 @@ #include #include +#include "sessionmanager/sessionmanager.h" + #define TR(x) QT_TRANSLATE_NOOP("Command line parser", QStringLiteral(x)) +using namespace Qt::StringLiterals; + static void disablePtrace() { #if !defined(DEVELOPMENT_BUILD) && defined(PR_SET_DUMPABLE) @@ -104,21 +107,8 @@ int main(int argc, char *argv[]) // Setup the environment setupEnvironment(); - // Automatically detect the right platform plugin to use -#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) - // Since Qt 5.11.0 we can specify a fallback platform plugin, - // so try wayland and xcb in this order unless it's running on a vt - qputenv("QT_QPA_PLATFORM", "wayland;xcb;aurora-eglfs"); -#else - // Try to detect the platform based on environment variables, - // fallback to liri if nothing is found - if (qEnvironmentVariableIsSet("WAYLAND_DISPLAY")) - qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("wayland")); - else if (qEnvironmentVariableIsSet("DISPLAY")) - qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("xcb")); - else - qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("aurora-eglfs")); -#endif + // Force platfrom to Aurora eglfs + qputenv("QT_QPA_PLATFORM", "aurora-eglfs"); // ShareOpenGLContexts is needed for using the threaded renderer // on NVIDIA EGLStreams and multi output compositors in general @@ -133,9 +123,6 @@ int main(int argc, char *argv[]) app.setOrganizationDomain(QStringLiteral("liri.io")); app.setQuitOnLastWindowClosed(false); - // Enable logs - QLoggingCategory::setFilterRules(QStringLiteral("qt.scenegraph.general=true")); - // Set style QQuickStyle::setStyle(QStringLiteral("Material")); @@ -197,21 +184,23 @@ int main(int argc, char *argv[]) // Print OS information qInfo("Platform name: %s", qPrintable(QGuiApplication::platformName())); - // Application - Application *shell = new Application(); - shell->setScreenConfigurationFileName(fakeScreenData); + // Session manager + auto *sessionManager = new SessionManager(); // Create the compositor and run + QQmlApplicationEngine engine; + engine.setInitialProperties({ { "SessionInterface"_L1, QVariant::fromValue(sessionManager) } }); + QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, + QCoreApplication::instance(), QCoreApplication::quit, Qt::QueuedConnection); bool urlAlreadySet = false; #ifdef DEVELOPMENT_BUILD if (parser.isSet(qmlOption)) { urlAlreadySet = true; - shell->setUrl(QUrl::fromLocalFile(parser.value(qmlOption))); + engine.load(QUrl::fromLocalFile(parser.value(qmlOption))); } #endif if (!urlAlreadySet) - shell->setUrl(QUrl(QStringLiteral("qrc:/qt/qml/compositor/qml/Compositor.qml"))); - QCoreApplication::postEvent(shell, new StartupEvent()); + engine.loadFromModule("LiriShell", "Compositor"); return app.exec(); } diff --git a/src/compositor/qml/Compositor.qml b/src/compositor/qml/Compositor.qml index 683cc810..0dd25ed2 100644 --- a/src/compositor/qml/Compositor.qml +++ b/src/compositor/qml/Compositor.qml @@ -13,6 +13,7 @@ import Aurora.Compositor.Wlroots import Aurora.Compositor.WlrLayerShell import Aurora.Compositor.XdgShell import Aurora.Compositor.XWayland as LXW +import Aurora.Platform import Liri.Session as Session import Liri.private.shell as P import Liri.Shell.Compositor as LS @@ -88,58 +89,19 @@ WaylandCompositor { * Output management */ - LS.ScreenModel { - id: screenModel - fileName: screenConfigurationFileName - } - - Component { - id: outputModeComponent - - WlrOutputModeV1 {} - } - - Component { - id: outputConfigComponent - - WlrOutputConfigurationV1 { - id: configuration - - onReadyToTest: { - if (!screenModel.testConfiguration(configuration)) - configuration.sendFailed(); - } - onReadyToApply: { - screenModel.applyConfiguration(configuration); - } - } - } - - WlrOutputManagerV1 { - id: outputManager - - onConfigurationRequested: { - var configuration = outputConfigComponent.createObject(outputManager); - configuration.initialize(outputManager, resource); - } + PlatformOutputsModel { + id: platformOutputsModel } Instantiator { - model: screenModel + model: platformOutputsModel delegate: Output { compositor: liriCompositor - screen: screenItem - position: screenItem.position - manufacturer: screenItem.manufacturer - model: screenItem.model - physicalSize: screenItem.physicalSize - subpixel: screenItem.subpixel - transform: screenItem.transform - scaleFactor: screenItem.scaleFactor + platformOutput: output Component.onCompleted: { // Set this as default output if configured - if (!liriCompositor.defaultOutput && screenItem.name === settings.outputs.primary) + if (!liriCompositor.defaultOutput && output.name === settings.outputs.primary) liriCompositor.defaultOutput = this; // Fallback to the first one @@ -163,36 +125,6 @@ WaylandCompositor { } } - Instantiator { - id: headManager - - model: screenModel - delegate: WlrOutputHeadV1 { - manager: outputManager - name: screenItem.name - description: screenItem.description - physicalSize: screenItem.physicalSize - position: screenItem.position - transform: screenItem.transform - scale: screenItem.scaleFactor - - Component.onCompleted: { - for (var i = 0; i < screenItem.modes.length; i++) { - var screenMode = screenItem.modes[i]; - var mode = outputModeComponent.createObject(this, {size: screenMode.resolution, refresh: screenMode.refreshRate}); - - addMode(mode); - - if (screenItem.preferredMode === screenMode) - preferredMode = mode; - - if (screenItem.currentMode === screenMode) - currentMode = mode; - } - } - } - } - /* * Extensions */ @@ -281,7 +213,8 @@ WaylandCompositor { focusPolicy: ExtSessionLockManagerV1.AutomaticFocus onLockSurfaceCreated: { - lockSurface.output.lockSurfacesModel.append({lockSurface: lockSurface}); + if (lockSurface.output) + lockSurface.output.lockSurfacesModel.append({lockSurface: lockSurface}); } } @@ -294,7 +227,8 @@ WaylandCompositor { var output = layerSurface.output; if (!output) output = liriCompositor.defaultOutput; - output.layerSurfacesModel.append({layerSurface: layerSurface, output}); + if (output) + output.layerSurfacesModel.append({layerSurface: layerSurface, output}); } } @@ -305,7 +239,7 @@ WaylandCompositor { } FluidDecorationManagerV1 { - onDecorationCreated: { + onDecorationCreated: (decoration) => { decoration.foregroundColorChanged.connect(function(color) { decoration.surface.foregroundColor = color; }); @@ -323,47 +257,6 @@ WaylandCompositor { // Screen copy - LS.ScreenCast { - id: screenCast - - onFrameAvailable: { - for (var i = 0; i < outputs.length; i++) { - var output = outputs[i]; - - if (output.screen.screen === screen) { - output.exportDmabufFrame.frame(size, offset, 0, 0, drmFormat, modifier, numObjects); - break; - } - } - } - onObjectAvailable: { - for (var i = 0; i < outputs.length; i++) { - var output = outputs[i]; - if (!output.exportDmabufFrame) - continue; - - if (output.screen.screen === screen) { - output.exportDmabufFrame.object(index, fd, size, offset, stride, planeIndex); - break; - } - } - } - onCaptureReady: { - for (var i = 0; i < outputs.length; i++) { - var output = outputs[i]; - if (!output.exportDmabufFrame) - continue; - - if (output.screen.screen === screen) { - output.exportDmabufFrame.ready(tv_sec, tv_nsec); - output.exportDmabufFrame = null; - screenCast.disable(screen); - break; - } - } - } - } - WlrExportDmabufManagerV1 { onOutputCaptureRequested: { if (frame.output.screen) { @@ -413,7 +306,7 @@ WaylandCompositor { property var toplevels: ([]) - onToplevelCreated: { + onToplevelCreated: (toplevel, xdgSurface) => { var window = xdgToplevelComponent.createObject(xdgShell, {xdgSurface: xdgSurface, toplevel: toplevel}); for (var i = 0; i < outputs.length; i++) outputs[i].currentWorkspace.shellSurfaces.append({shellSurface: xdgSurface, window: window, output: outputs[i]}); diff --git a/src/compositor/qml/desktop/Output.qml b/src/compositor/qml/desktop/Output.qml index 3051892f..65269de9 100644 --- a/src/compositor/qml/desktop/Output.qml +++ b/src/compositor/qml/desktop/Output.qml @@ -7,10 +7,10 @@ import QtQuick.Window import QtQuick.Controls import QtQuick.Controls.Material import Aurora.Compositor -import Liri.Shell.Compositor as LS +import Aurora.Platform import Liri.Session as Session -LS.WaylandOutput { +WaylandOutput { id: output readonly property bool primary: liriCompositor.defaultOutput === this @@ -28,10 +28,9 @@ LS.WaylandOutput { property bool __idle: false - sizeFollowsWindow: false automaticFrameCallback: { - if (screen) - return screen.enabled && screen.powerState === LS.ScreenItem.PowerStateOn; + if (platformOutput) + return platformOutput.enabled && platformOutput.powerState === PlatformOutput.PowerState.On; return true; } @@ -53,7 +52,7 @@ LS.WaylandOutput { console.debug("Power on output", manufacturer, model); idleDimmer.fadeOut(); - screen.powerState = LS.ScreenItem.PowerStateOn; + platformOutput.powerState = PlatformOutput.PowerState.On; __idle = false; } diff --git a/src/compositor/qml/desktop/OutputWindow.qml b/src/compositor/qml/desktop/OutputWindow.qml index 4a14de34..cafea787 100644 --- a/src/compositor/qml/desktop/OutputWindow.qml +++ b/src/compositor/qml/desktop/OutputWindow.qml @@ -30,11 +30,11 @@ Window { width: output.geometry.width height: output.geometry.height flags: Qt.Window | Qt.FramelessWindowHint - screen: output.screen ? Qt.application.screens[output.screen.screenIndex] : null + screen: output.platformOutput?.screen || null color: splashVisible ? Material.color(Material.BlueGrey, Material.Shade800) : !splashVisible && shellHelper.isReady ? Material.color(Material.Grey, Material.Shade700) : "black" - visible: output.screen && output.screen.enabled + visible: output.platformOutput && output.platformOutput.enabled // Keyboard handling LS.KeyEventFilter { diff --git a/src/compositor/qml/error/ErrorCompositor.qml b/src/compositor/qml/error/ErrorCompositor.qml deleted file mode 100644 index 7fb33d7b..00000000 --- a/src/compositor/qml/error/ErrorCompositor.qml +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-FileCopyrightText: 2018 Pier Luigi Fiorini -// -// SPDX-License-Identifier: GPL-3.0-or-later - -import QtQml -import QtQuick -import QtQuick.Window -import Aurora.Compositor -import Liri.Shell.Compositor as LS -import Liri.Session as Session -import ".." as Root - -WaylandCompositor { - id: liriCompositor - - onCreatedChanged: { - if (liriCompositor.created) { - console.debug("Compositor created"); - - Session.SessionManager.setEnvironment("WAYLAND_DISPLAY", liriCompositor.socketName); - SessionInterface.registerService(); - } - } - - onSurfaceRequested: { - var surface = surfaceComponent.createObject(liriCompositor, {}); - surface.initialize(liriCompositor, client, id, version); - } - - Shortcut { - context: Qt.ApplicationShortcut - sequence: "Ctrl+Alt+Backspace" - onActivated: liriCompositor.quit() - } - - LS.ScreenModel { - id: screenModel - fileName: screenConfigurationFileName - } - - Instantiator { - model: screenModel - delegate: ErrorOutput { - compositor: liriCompositor - screen: screenItem - position: screenItem.position - manufacturer: screenItem.manufacturer - model: screenItem.model - physicalSize: screenItem.physicalSize - subpixel: screenItem.subpixel - transform: screenItem.transform - scaleFactor: screenItem.scaleFactor - - Component.onCompleted: { - // Set default output the first time - if (!liriCompositor.defaultOutput) - liriCompositor.defaultOutput = this; - } - } - } - - // Surface component - Component { - id: surfaceComponent - - WaylandSurface {} - } - - // Shell helper - LS.LiriShellV1 { - id: shellHelper - } - - function quit() { - shellHelper.sendQuit(); - - for (var i = 0; i < outputs.length; i++) - outputs[i].window.close(); - - Qt.quit(); - } -} diff --git a/src/compositor/qml/error/ErrorOutput.qml b/src/compositor/qml/error/ErrorOutput.qml deleted file mode 100644 index f31bce37..00000000 --- a/src/compositor/qml/error/ErrorOutput.qml +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: 2018 Pier Luigi Fiorini -// -// SPDX-License-Identifier: GPL-3.0-or-later - -import QtQuick -import QtQuick.Window -import Aurora.Compositor -import Liri.Shell.Compositor as LS - -LS.WaylandOutput { - id: output - - readonly property bool primary: liriCompositor.defaultOutput === this - - window: Window { - id: outputWindow - - x: output.position.x - y: output.position.y - width: output.geometry.width - height: output.geometry.height - flags: Qt.Window | Qt.FramelessWindowHint - screen: output.screen ? Qt.application.screens[output.screen.screenIndex] : null - color: "black" - visible: true - - WaylandMouseTracker { - id: mouseTracker - - anchors.fill: parent - windowSystemCursorEnabled: containsMouse - - ErrorScreenView { - id: screenView - anchors.fill: parent - } - - LS.WaylandCursorGrabber { - seat: output.compositor.defaultSeat - grab: mouseTracker.containsMouse - } - } - } -} diff --git a/src/compositor/qml/error/ErrorScreenView.qml b/src/compositor/qml/error/ErrorScreenView.qml deleted file mode 100644 index 95eebbb6..00000000 --- a/src/compositor/qml/error/ErrorScreenView.qml +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-FileCopyrightText: 2018 Pier Luigi Fiorini -// SPDX-FileCopyrightText: 2017 Michael Spencer -// -// SPDX-License-Identifier: GPL-3.0-or-later - -import QtQuick -import QtQuick.Layouts -import QtQuick.Controls -import QtQuick.Controls.Material -import Fluid.Controls as FluidControls - -Rectangle { - Material.theme: Material.Dark - - color: Material.color(Material.Red) - - ColumnLayout { - anchors.centerIn: parent - - spacing: 0 - - FluidControls.Icon { - source: FluidControls.Utils.iconUrl("alert/warning") - size: 96 - - Layout.alignment: Qt.AlignHCenter - } - - Item { - Layout.minimumHeight: 8 - } - - FluidControls.DisplayLabel { - text: qsTr("Oh no!") - color: Material.primaryTextColor - - Layout.alignment: Qt.AlignHCenter - } - - FluidControls.SubheadingLabel { - text: qsTr("Something went wrong and the desktop failed to load.") - color: Material.primaryTextColor - - Layout.alignment: Qt.AlignHCenter - } - - Item { - Layout.minimumHeight: 24 - } - - Button { - text: qsTr("Quit") - onClicked: liriCompositor.quit() - - Layout.alignment: Qt.AlignHCenter - - Material.background: "white" - Material.theme: Material.Light - Material.elevation: 2 - } - } -} diff --git a/src/compositor/qml/windows/Chrome.qml b/src/compositor/qml/windows/Chrome.qml index bb1ce3e5..529b5330 100644 --- a/src/compositor/qml/windows/Chrome.qml +++ b/src/compositor/qml/windows/Chrome.qml @@ -134,7 +134,7 @@ LS.ChromeItem { // Always cache the dropshadow cache: true - source: "qrc:/images/chrome/dropshadow-" + (window.activated ? "24" : "8") + ".png" + source: "qrc:/qt/qml/LiriShell/images/chrome/dropshadow-" + (window.activated ? "24" : "8") + ".png" visible: window.bordered } diff --git a/src/compositor/qml/windows/TitleBar.qml b/src/compositor/qml/windows/TitleBar.qml index 17a619cd..b3cfcec0 100644 --- a/src/compositor/qml/windows/TitleBar.qml +++ b/src/compositor/qml/windows/TitleBar.qml @@ -70,7 +70,7 @@ LS.AbstractTitleBar { spacing: 12 DecorationButton { - icon.source: "qrc:/images/chrome/window-minimize.svg" + icon.source: "qrc:/qt/qml/LiriShell/images/chrome/window-minimize.svg" icon.color: chooseColor(foregroundColor, defaultForegroundColor) onClicked: { titleBar.minimizeClicked(); @@ -78,7 +78,7 @@ LS.AbstractTitleBar { } DecorationButton { - icon.source: "qrc:/images/chrome/" + (window.maximized ? "window-restore.svg" : "window-maximize.svg") + icon.source: "qrc:/qt/qml/LiriShell/images/chrome/" + (window.maximized ? "window-restore.svg" : "window-maximize.svg") icon.color: chooseColor(foregroundColor, defaultForegroundColor) visible: maximizable onClicked: { @@ -88,7 +88,7 @@ LS.AbstractTitleBar { } DecorationButton { - icon.source: "qrc:/images/chrome/window-close.svg" + icon.source: "qrc:/qt/qml/LiriShell/images/chrome/window-close.svg" icon.color: chooseColor(foregroundColor, defaultForegroundColor) onClicked: { titleBar.closeClicked(); diff --git a/src/helper/CMakeLists.txt b/src/helper/CMakeLists.txt index 498e4cb3..4d2021bc 100644 --- a/src/helper/CMakeLists.txt +++ b/src/helper/CMakeLists.txt @@ -13,7 +13,7 @@ add_executable(LiriShellHelper main.cpp) file(GLOB_RECURSE _qml_files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" CMAKE_CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/qml/*.qml") file(GLOB_RECURSE _image_files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" CMAKE_CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/images/*") qt6_add_qml_module(LiriShellHelper - URI helper + URI LiriShellHelper QML_FILES ${_qml_files} RESOURCES ${_image_files} ) diff --git a/src/helper/main.cpp b/src/helper/main.cpp index 134ace5d..1620e1fe 100644 --- a/src/helper/main.cpp +++ b/src/helper/main.cpp @@ -92,8 +92,10 @@ int main(int argc, char *argv[]) setupSystemd(); // Create UI - QSharedPointer engine(new QQmlApplicationEngine); - engine->load(QStringLiteral("qrc:/qt/qml/helper/qml/main.qml")); + QQmlApplicationEngine engine; + engine.loadFromModule("LiriShellHelper", "Main"); + if (engine.rootObjects().isEmpty()) + return 1; return app.exec(); } diff --git a/src/helper/qml/main.qml b/src/helper/qml/Main.qml similarity index 100% rename from src/helper/qml/main.qml rename to src/helper/qml/Main.qml diff --git a/src/helper/qml/PowerOffDialog.qml b/src/helper/qml/PowerOffDialog.qml index 8596b1b7..e9c0250b 100644 --- a/src/helper/qml/PowerOffDialog.qml +++ b/src/helper/qml/PowerOffDialog.qml @@ -120,7 +120,7 @@ Window { } FluidControls.ListItem { - icon.source: Qt.resolvedUrl("qrc:/qt/qml/helper/images/sleep.svg") + icon.source: Qt.resolvedUrl("qrc:/qt/qml/LiriShellHelper/images/sleep.svg") text: qsTr("Sleep") visible: LiriDevice.LocalDevice.canSuspend onClicked: { @@ -150,7 +150,7 @@ Window { } FluidControls.ListItem { - icon.source: Qt.resolvedUrl("qrc:/qt/qml/helper/images/reload.svg") + icon.source: Qt.resolvedUrl("qrc:/qt/qml/LiriShellHelper/images/reload.svg") text: qsTr("Restart") visible: LiriDevice.LocalDevice.canRestart onClicked: { diff --git a/src/helper/qml/panel/StatusMenu.qml b/src/helper/qml/panel/StatusMenu.qml index 224e1e72..2d2b80d7 100644 --- a/src/helper/qml/panel/StatusMenu.qml +++ b/src/helper/qml/panel/StatusMenu.qml @@ -58,7 +58,7 @@ Dialog { } ToolButton { - icon.source: Qt.resolvedUrl("qrc:/qt/qml/helper/images/logout.svg") + icon.source: Qt.resolvedUrl("qrc:/qt/qml/LiriShellHelper/images/logout.svg") onClicked: { panelMenu.logoutRequested(); } diff --git a/src/imports/compositor/CMakeLists.txt b/src/imports/compositor/CMakeLists.txt index 62299f35..d7cceb25 100644 --- a/src/imports/compositor/CMakeLists.txt +++ b/src/imports/compositor/CMakeLists.txt @@ -1,9 +1,6 @@ # SPDX-FileCopyrightText: 2024 Pier Luigi Fiorini # SPDX-License-Identifier: BSD-3-Clause -if(NOT TARGET Liri::AuroraPlatformHeaders) - find_package(Liri1AuroraPlatformHeaders REQUIRED) -endif() if(NOT TARGET Liri::AuroraCompositor) find_package(Liri1AuroraCompositor REQUIRED) endif() @@ -30,9 +27,6 @@ target_sources(LiriShellCompositorQmlPlugin hotspot.cpp hotspot.h keyeventfilter.cpp keyeventfilter.h lirishellcompositorplugin.cpp - quickoutput.cpp quickoutput.h - screencast.cpp screencast.h - screenmodel.cpp screenmodel.h waylandcursorgrabber.cpp waylandcursorgrabber.h waylandliricolorpickerv1.cpp waylandliricolorpickerv1.h waylandliricolorpickerv1_p.h waylandlirimodalv1.cpp waylandlirimodalv1.h waylandlirimodalv1_p.h @@ -63,7 +57,6 @@ target_link_libraries(LiriShellCompositorQmlPlugin Qt6::GuiPrivate Qt6::Qml Qt6::Quick - Liri::AuroraPlatformHeaders Liri::AuroraCompositor Liri::Xdg ) diff --git a/src/imports/compositor/lirishellcompositorplugin.cpp b/src/imports/compositor/lirishellcompositorplugin.cpp index 94dafc3b..9d984eda 100644 --- a/src/imports/compositor/lirishellcompositorplugin.cpp +++ b/src/imports/compositor/lirishellcompositorplugin.cpp @@ -15,9 +15,6 @@ #include "helperlauncher.h" #include "hotspot.h" #include "keyeventfilter.h" -#include "quickoutput.h" -#include "screencast.h" -#include "screenmodel.h" #include "waylandcursorgrabber.h" #include "waylandliricolorpickerv1_p.h" #include "waylandlirimodalv1.h" @@ -64,13 +61,6 @@ class LiriShellCompositorPlugin : public QQmlExtensionPlugin qmlRegisterType(uri, 1, 0, "LiriShellV1"); qmlRegisterType(uri, 1, 0, "LiriOsdV1"); qmlRegisterUncreatableType(uri, 1, 0, "LiriShortcutV1", QObject::tr("Cannot create instance of LiriShortcutV1")); - qmlRegisterType(uri, 1, 0, "WaylandOutput"); - qmlRegisterType(uri, 1, 0, "ScreenCast"); - qmlRegisterType(uri, 1, 0, "ScreenModel"); - qmlRegisterUncreatableType(uri, 1, 0, "ScreenMode", - QObject::tr("Cannot create instance of ScreenMode")); - qmlRegisterUncreatableType(uri, 1, 0, "ScreenItem", - QObject::tr("Cannot create instance of ScreenItem")); qmlRegisterType(uri, 1, 0, "WindowMouseTracker"); } diff --git a/src/imports/compositor/quickoutput.cpp b/src/imports/compositor/quickoutput.cpp deleted file mode 100644 index bb12ad44..00000000 --- a/src/imports/compositor/quickoutput.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-FileCopyrightText: 2019 Pier Luigi Fiorini -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "lirishellcompositorlogging.h" -#include "quickoutput.h" - -QuickOutput::QuickOutput() - : Aurora::Compositor::WaylandQuickOutput() -{ -} - -ScreenItem *QuickOutput::screen() const -{ - return m_screen; -} - -void QuickOutput::setScreen(ScreenItem *screen) -{ - if (m_screen == screen) - return; - - if (m_screen) { - if (m_initialized) { - qCWarning(lcShellCompositor, "Cannot change WaylandOutput::screen after the component is complete"); - return; - } else { - disconnect(this, SLOT(handleCurrentModeChanged(QSize,int))); - } - } - - m_screen = screen; - connect(m_screen, SIGNAL(currentModeChanged(QSize,int)), - this, SLOT(handleCurrentModeChanged(QSize,int))); - addModes(); - Q_EMIT screenChanged(screen); -} - -void QuickOutput::componentComplete() -{ - m_initialized = true; - WaylandQuickOutput::componentComplete(); -} - -void QuickOutput::addModes() -{ - auto modes = m_screen->modes(); - for (auto *mode : qAsConst(modes)) - addScreenMode(mode); -} - -void QuickOutput::addScreenMode(ScreenMode *mode) -{ - auto isPreferred = m_screen->preferredMode()->resolution() == mode->resolution() && - m_screen->preferredMode()->refreshRate() == mode->refreshRate(); - auto isCurrent = m_screen->currentMode()->resolution() == mode->resolution() && - m_screen->currentMode()->refreshRate() == mode->refreshRate(); - WaylandOutputMode wlMode(mode->resolution(), mode->refreshRate()); - - bool alreadyHasMode = false; - const auto modesList = modes(); - for (const auto &mode : modesList) { - if (mode == wlMode) { - alreadyHasMode = true; - break; - } - } - - if (!alreadyHasMode) - addMode(wlMode, isPreferred); - - if (isCurrent) - setCurrentMode(wlMode); -} - -void QuickOutput::handleCurrentModeChanged(const QSize &resolution, int refreshRate) -{ - setCurrentMode(WaylandOutputMode(resolution, refreshRate)); -} diff --git a/src/imports/compositor/quickoutput.h b/src/imports/compositor/quickoutput.h deleted file mode 100644 index 079d8079..00000000 --- a/src/imports/compositor/quickoutput.h +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: 2019 Pier Luigi Fiorini -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef QUICKOUTPUT_H -#define QUICKOUTPUT_H - -#include - -#include "screenmodel.h" - -class QuickOutput : public Aurora::Compositor::WaylandQuickOutput -{ - Q_OBJECT - Q_PROPERTY(ScreenItem *screen READ screen WRITE setScreen NOTIFY screenChanged) -public: - explicit QuickOutput(); - - ScreenItem *screen() const; - void setScreen(ScreenItem *screen); - -Q_SIGNALS: - void screenChanged(ScreenItem *screen); - -protected: - void componentComplete() override; - -private: - bool m_initialized = false; - ScreenItem *m_screen = nullptr; - - void addModes(); - void addScreenMode(ScreenMode *mode); - -private Q_SLOTS: - void handleCurrentModeChanged(const QSize &resolution, int refreshRate); -}; - -#endif // QUICKOUTPUT_H diff --git a/src/imports/compositor/screencast.cpp b/src/imports/compositor/screencast.cpp deleted file mode 100644 index 49f7acdc..00000000 --- a/src/imports/compositor/screencast.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-FileCopyrightText: 2020 Pier Luigi Fiorini -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#include -#include - -#include - -#include "lirishellcompositorlogging.h" -#include "screencast.h" - -ScreenCast::ScreenCast(QObject *parent) - : QObject(parent) -{ - QGuiApplication::instance()->installEventFilter(this); -} - -void ScreenCast::enable(QScreen *screen) -{ - if (QGuiApplication::platformName() != QLatin1String("liri")) { - qCWarning(lcShellCompositor, "Cannot enable screencast: " \ - "unsupported platform \"%s\", please use \"liri\"", - qPrintable(QGuiApplication::platformName())); - return; - } - - Aurora::PlatformSupport::EglFSFunctions::enableScreenCast(screen); -} - -void ScreenCast::disable(QScreen *screen) -{ - if (QGuiApplication::platformName() != QLatin1String("liri")) { - qCWarning(lcShellCompositor, "Cannot disable screencast: " \ - "unsupported platform \"%s\", please use \"liri\"", - qPrintable(QGuiApplication::platformName())); - return; - } - - Aurora::PlatformSupport::EglFSFunctions::disableScreenCast(screen); -} - -bool ScreenCast::eventFilter(QObject *obj, QEvent *event) -{ - if (event->type() == Aurora::PlatformSupport::ScreenCastFrameEvent::registeredType()) { - auto *e = static_cast(event); - Q_EMIT frameAvailable(e->screen, e->size, e->offset, e->drmFormat, e->modifier, e->numObjects); - return true; - } else if (event->type() == Aurora::PlatformSupport::ScreenCastObjectEvent::registeredType()) { - auto *e = static_cast(event); - Q_EMIT objectAvailable(e->screen, e->index, e->fd, e->size, e->offset, e->stride, e->planeIndex); - return true; - } else if (event->type() == Aurora::PlatformSupport::ScreenCastReadyEvent::registeredType()) { - auto *e = static_cast(event); - Q_EMIT captureReady(e->screen, e->tv_sec, e->tv_nsec); - return true; - } - - return QObject::eventFilter(obj, event); -} diff --git a/src/imports/compositor/screencast.h b/src/imports/compositor/screencast.h deleted file mode 100644 index 3e982720..00000000 --- a/src/imports/compositor/screencast.h +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-FileCopyrightText: 2020 Pier Luigi Fiorini -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef SCREENCAST_H -#define SCREENCAST_H - -#include - -QT_FORWARD_DECLARE_CLASS(QScreen) - -class ScreenCast : public QObject -{ - Q_OBJECT -public: - explicit ScreenCast(QObject *parent = nullptr); - - Q_INVOKABLE void enable(QScreen *screen); - Q_INVOKABLE void disable(QScreen *screen); - -Q_SIGNALS: - void frameAvailable(QScreen *screen, - const QSize &size, const QPoint &offset, - quint32 drmFormat, quint64 modifier, - quint32 numObjects); - void objectAvailable(QScreen *screen, - quint32 index, quint32 fd, quint32 size, - quint32 offset, quint32 stride, - quint32 planeIndex); - void captureReady(QScreen *screen, quint64 tv_sec, quint32 tv_nsec); - -protected: - bool eventFilter(QObject *obj, QEvent *event) override; -}; - -#endif // SCREENCAST_H diff --git a/src/imports/compositor/screenmodel.cpp b/src/imports/compositor/screenmodel.cpp deleted file mode 100644 index 7174c4e3..00000000 --- a/src/imports/compositor/screenmodel.cpp +++ /dev/null @@ -1,714 +0,0 @@ -// SPDX-FileCopyrightText: 2018 Pier Luigi Fiorini -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "lirishellcompositorlogging.h" -#include "screenmodel.h" - -static ScreenItem::PowerState convertPowerState(Aurora::PlatformSupport::EglFSFunctions::PowerState powerState) -{ - switch (powerState) { - case Aurora::PlatformSupport::EglFSFunctions::PowerStateOn: - return ScreenItem::PowerStateOn; - case Aurora::PlatformSupport::EglFSFunctions::PowerStateStandby: - return ScreenItem::PowerStateStandby; - case Aurora::PlatformSupport::EglFSFunctions::PowerStateSuspend: - return ScreenItem::PowerStateSuspend; - case Aurora::PlatformSupport::EglFSFunctions::PowerStateOff: - return ScreenItem::PowerStateOff; - } - Q_UNREACHABLE(); -} - -static Aurora::PlatformSupport::EglFSFunctions::PowerState convertPowerState(ScreenItem::PowerState powerState) -{ - switch (powerState) { - case ScreenItem::PowerStateOn: - return Aurora::PlatformSupport::EglFSFunctions::PowerStateOn; - case ScreenItem::PowerStateStandby: - return Aurora::PlatformSupport::EglFSFunctions::PowerStateStandby; - case ScreenItem::PowerStateSuspend: - return Aurora::PlatformSupport::EglFSFunctions::PowerStateSuspend; - case ScreenItem::PowerStateOff: - return Aurora::PlatformSupport::EglFSFunctions::PowerStateOff; - } - Q_UNREACHABLE(); -} - -/* - * ScreenMode - */ - - -ScreenMode::ScreenMode(QObject *parent) - : QObject(parent) -{ -} - -QSize ScreenMode::resolution() const -{ - return m_resolution; -} - -int ScreenMode::refreshRate() const -{ - return m_refreshRate; -} - -bool ScreenMode::operator==(const ScreenMode &other) const -{ - return resolution() == other.resolution() && - refreshRate() == other.refreshRate(); -} - -/* - * ScreenItem - */ - -ScreenItem::ScreenItem(QObject *parent) - : QObject(parent) -{ -} - -ScreenItem::~ScreenItem() -{ - qDeleteAll(m_modes); -} - -QScreen *ScreenItem::screen() const -{ - return m_screen; -} - -int ScreenItem::screenIndex() const -{ - return qGuiApp->screens().indexOf(m_screen); -} - -bool ScreenItem::isEnabled() const -{ - return m_enabled; -} - -QString ScreenItem::manufacturer() const -{ - return m_manufacturer; -} - -QString ScreenItem::model() const -{ - return m_model; -} - -QString ScreenItem::name() const -{ - return m_name; -} - -QString ScreenItem::description() const -{ - return m_description; -} - -QPoint ScreenItem::position() const -{ - return m_position; -} - -QSizeF ScreenItem::physicalSize() const -{ - return m_physicalSize; -} - -qreal ScreenItem::scaleFactor() const -{ - return m_scaleFactor; -} - -WaylandOutput::Subpixel ScreenItem::subpixel() const -{ - return m_subpixel; -} - -WaylandOutput::Transform ScreenItem::transform() const -{ - return m_transform; -} - -QVector ScreenItem::modes() const -{ - return m_modes; -} - -QQmlListProperty ScreenItem::modesList() -{ - auto countFunc = [](QQmlListProperty *prop) { - return static_cast(prop->object)->m_modes.count(); - }; - auto atFunc = [](QQmlListProperty *prop, qsizetype index) { - return static_cast(prop->object)->m_modes.at(index); - }; - return QQmlListProperty(this, this, countFunc, atFunc); -} - -ScreenMode *ScreenItem::currentMode() const -{ - return m_currentMode; -} - -ScreenMode *ScreenItem::preferredMode() const -{ - return m_preferredMode; -} - -ScreenItem::PowerState ScreenItem::powerState() const -{ - if (!m_screen) { - qCWarning(lcShellCompositor) << "ScreenItem cannot get power state if the screen property is not set"; - return ScreenItem::PowerStateOn; - } - - return convertPowerState(Aurora::PlatformSupport::EglFSFunctions::getPowerState(m_screen)); -} - -void ScreenItem::setPowerState(ScreenItem::PowerState state) -{ - if (!m_screen) { - qCWarning(lcShellCompositor) << "ScreenItem cannot set power state if the screen property is not set"; - return; - } - - auto oldPowerState = Aurora::PlatformSupport::EglFSFunctions::getPowerState(m_screen); - auto newPowerState = convertPowerState(state); - - if (oldPowerState == newPowerState) - return; - - Aurora::PlatformSupport::EglFSFunctions::setPowerState(m_screen, newPowerState); - - Q_EMIT powerStateChanged(); -} - -/* - * ScreenModel - */ - -ScreenModel::ScreenModel(QObject *parent) - : QAbstractListModel(parent) -{ -} - -ScreenModel::~ScreenModel() -{ - qDeleteAll(m_items); -} - -QString ScreenModel::fileName() const -{ - return m_fileName; -} - -void ScreenModel::setFileName(const QString &fileName) -{ - if (fileName == m_fileName) - return; - - // We can feed the model only once - if (m_initialized) { - qCWarning(lcShellCompositor, "Screen configuration cannot be changed after initialization"); - return; - } - - m_fileName = fileName; - Q_EMIT fileNameChanged(); -} - -int ScreenModel::count() const -{ - return rowCount(); -} - -ScreenItem *ScreenModel::get(int index) const -{ - return m_items.at(index); -} - -QHash ScreenModel::roleNames() const -{ - QHash roles; - roles.insert(ScreenItemRole, QByteArrayLiteral("screenItem")); - roles.insert(ScreenRole, QByteArrayLiteral("screen")); - roles.insert(ScreenIndexRole, QByteArrayLiteral("screenIndex")); - return roles; -} - -int ScreenModel::rowCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent); - return m_items.count(); -} - -QVariant ScreenModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - ScreenItem *item = m_items.at(index.row()); - - switch (role) { - case ScreenItemRole: - return QVariant::fromValue(item); - case ScreenRole: - return QVariant::fromValue(item->screen()); - case ScreenIndexRole: - return item->screenIndex(); - } - - return QVariant(); -} - -bool ScreenModel::testConfiguration(WaylandWlrOutputConfigurationV1 *configuration) -{ - if (!m_fileName.isEmpty()) { - qCWarning(lcShellCompositor, "Cannot test output configuration when using a fake screen backend"); - return false; - } - - const auto changesList = configuration->enabledHeads(); - for (auto *changes : changesList) { - for (auto *item : qAsConst(m_items)) { - if (item->name() == changes->head()->name()) { - QSize size; - quint32 refresh = 0; - - if (changes->customModeSize().isValid() && changes->customModeRefresh() > 0) { - size = changes->customModeSize(); - refresh = changes->customModeRefresh(); - } else if (changes->mode()) { - size = changes->mode()->size(); - refresh = changes->mode()->refresh(); - } - - bool modeFound = false; - const auto modes = item->modes(); - for (auto *screenMode : modes) { - if (screenMode->resolution() == size && screenMode->refreshRate() == refresh) { - modeFound = true; - break; - } - } - - if (!modeFound) { - qCWarning(lcShellCompositor, "Output configuration test failed: mode not found"); - return false; - } - - break; - } - } - } - - return true; -} - -void ScreenModel::applyConfiguration(WaylandWlrOutputConfigurationV1 *configuration) -{ - if (!m_fileName.isEmpty()) { - qCWarning(lcShellCompositor, "Cannot test or apply output configuration when using a fake screen backend"); - return; - } - - QVector screenChanges; - - const auto changesList = configuration->enabledHeads(); - for (auto *changes : changesList) { - for (auto *item : qAsConst(m_items)) { - if (item->name() == changes->head()->name()) { - Aurora::PlatformSupport::ScreenChange screenChange; - screenChange.screen = item->screen(); - screenChange.enabled = true; - screenChange.position = changes->position(); - if (changes->customModeSize().isValid() && changes->customModeRefresh() > 0) { - screenChange.resolution = changes->customModeSize(); - screenChange.refreshRate = changes->customModeRefresh(); - } else if (changes->mode()) { - screenChange.resolution = changes->mode()->size(); - screenChange.refreshRate = changes->mode()->refresh(); - } - screenChange.scale = changes->scale(); - screenChanges.append(screenChange); - - if (!item->isEnabled()) { - item->m_enabled = true; - Q_EMIT item->enabledChanged(); - } - - break; - } - } - } - - const auto heads = configuration->disabledHeads(); - for (auto *head : heads) { - for (auto *item : qAsConst(m_items)) { - if (item->name() == head->name()) { - Aurora::PlatformSupport::ScreenChange screenChange; - screenChange.screen = item->screen(); - screenChange.enabled = false; - screenChanges.append(screenChange); - - if (item->isEnabled()) { - item->m_enabled = false; - Q_EMIT item->enabledChanged(); - } - - break; - } - } - } - - bool result = Aurora::PlatformSupport::EglFSFunctions::applyScreenChanges(screenChanges); - - // TODO: Send cancelled if a new screen is added or changed meanwhile - - if (result) - configuration->sendSucceeded(); - else - configuration->sendFailed(); -} - -void ScreenModel::classBegin() -{ -} - -void ScreenModel::componentComplete() -{ - // When the component is complete we decide if we want to feed - // the model with actual screens or the JSON configuration - // provided by the user - - if (m_fileName.isEmpty()) { - const auto screens = qGuiApp->screens(); - for (auto *screen : screens) - handleScreenAdded(screen); - Q_EMIT countChanged(); - - connect(qGuiApp, &QGuiApplication::screenAdded, this, &ScreenModel::handleScreenAdded); - connect(qGuiApp, &QGuiApplication::screenRemoved, this, [this](QScreen *screen) { - int row = 0; - auto it = m_items.begin(); - while (it != m_items.end()) { - auto screenItem = (*it); - if (screenItem->screen() == screen) { - beginRemoveRows(QModelIndex(), row, row); - it = m_items.erase(it); - endRemoveRows(); - screenItem->deleteLater(); - } else { - ++it; - } - row++; - } - }); - } else { - addFakeScreens(); - } -} - -void ScreenModel::addFakeScreens() -{ - QFile file(m_fileName); - if (!file.open(QFile::ReadOnly)) { - qCWarning(lcShellCompositor) << "Could not open configuration file" - << m_fileName << "for reading"; - return; - } - - QByteArray data = file.readAll(); - - file.close(); - - const QJsonDocument doc = QJsonDocument::fromJson(data); - if (!doc.isObject()) { - qCWarning(lcShellCompositor, "Error parsing \"%s\": no top-level JSON object", - qPrintable(m_fileName)); - return; - } - - const QJsonObject object = doc.object(); - const QJsonArray outputs = object.value(QStringLiteral("outputs")).toArray(); - - for (int i = 0; i < outputs.size(); i++) { - const QVariantMap outputSettings = outputs.at(i).toObject().toVariantMap(); - qCDebug(lcShellCompositor) << "Output settings:" << outputSettings; - - QString name = outputSettings.value(QStringLiteral("name")).toString(); - if (name.isEmpty()) { - if (QGuiApplication::platformName() == QLatin1String("xcb")) - name = QStringLiteral("X11-"); - else if (QGuiApplication::platformName().contains(QLatin1String("wayland"))) - name = QStringLiteral("WL-"); - else - name = QStringLiteral("UNK-"); - name.append(QString::number(i)); - } - qCDebug(lcShellCompositor) << "Output name:" << name; - - QString description = outputSettings.value(QStringLiteral("description")).toString(); - if (description.isEmpty()) { - if (QGuiApplication::platformName() == QLatin1String("xcb")) - description = QStringLiteral("Virtual X11 output via :%1").arg(QString::fromLocal8Bit(qgetenv("DISPLAY"))); - else if (QGuiApplication::platformName().contains(QLatin1String("wayland"))) - description = QStringLiteral("Virtual Wayland output"); - else - description = QStringLiteral("Virtual output"); - } - qCDebug(lcShellCompositor) << "Output description:" << description; - - int scale = qMax(1, outputSettings.value(QStringLiteral("scale")).toInt()); - qCDebug(lcShellCompositor) << "Scale:" << scale; - - const QVariantMap posValue = outputSettings.value(QStringLiteral("position")).toMap(); - int x = posValue.value(QStringLiteral("x")).toInt(); - int y = posValue.value(QStringLiteral("y")).toInt(); - QPoint pos(x, y); - qCDebug(lcShellCompositor) << "Output position:" << pos; - - const QVariantMap modeValue = outputSettings.value(QStringLiteral("mode")).toMap(); - const QVariantMap sizeValue = modeValue.value(QStringLiteral("size")).toMap(); - int w = sizeValue.value(QStringLiteral("width")).toInt(); - int h = sizeValue.value(QStringLiteral("height")).toInt(); - QSize size = QSize(w, h); - int refreshRate = modeValue.value(QStringLiteral("refreshRate")).toInt(); - qCDebug(lcShellCompositor) << "Output size:" << size; - qCDebug(lcShellCompositor) << "Output refresh rate:" << refreshRate; - - const QVariantMap physicalSizeValue = outputSettings.value(QStringLiteral("physicalSize")).toMap(); - QSizeF physicalSize; - physicalSize.setWidth(physicalSizeValue.value(QStringLiteral("width"), -1).toInt()); - physicalSize.setHeight(physicalSizeValue.value(QStringLiteral("height"), -1).toInt()); - if (!physicalSize.isValid()) { - physicalSize.setWidth(w * 0.26458); - physicalSize.setHeight(h * 0.26458); - } - qCDebug(lcShellCompositor) << "Physical size millimiters:" << physicalSize; - - Qt::ScreenOrientation orientation = - static_cast(outputSettings.value(QStringLiteral("orientation")).toInt()); - qCDebug(lcShellCompositor) << "Output orientation:" << orientation; - - beginInsertRows(QModelIndex(), m_items.count(), m_items.count()); - - ScreenItem *item = new ScreenItem(this); - item->m_enabled = true; - item->m_manufacturer = QStringLiteral("Liri"); - item->m_model = name; - item->m_name = name; - item->m_description = description; - if (physicalSize.isValid()) - item->m_physicalSize = physicalSize; - item->m_position = pos; - item->m_scaleFactor = scale; - - switch (orientation) { - case Qt::PortraitOrientation: - item->m_transform = WaylandOutput::Transform90; - break; - case Qt::InvertedLandscapeOrientation: - item->m_transform = WaylandOutput::Transform180; - break; - case Qt::InvertedPortraitOrientation: - item->m_transform = WaylandOutput::Transform270; - break; - default: - break; - } - - ScreenMode *mode = new ScreenMode(item); - mode->m_resolution = size; - mode->m_refreshRate = refreshRate; - item->m_modes.append(mode); - - m_items.append(item); - - endInsertRows(); - } - - Q_EMIT countChanged(); -} - -void ScreenModel::handleScreenAdded(QScreen *screen) -{ - beginInsertRows(QModelIndex(), m_items.count(), m_items.count()); - - ScreenItem *item = new ScreenItem(this); - item->m_screen = screen; - - item->m_manufacturer = screen->manufacturer(); - if (item->m_manufacturer.isEmpty()) - item->m_manufacturer = QStringLiteral("Unknown"); - - if (!screen->model().isEmpty()) { - item->m_model = screen->model(); - if (!screen->serialNumber().isEmpty()) { - item->m_model.append(QLatin1Char('/')); - item->m_model.append(screen->serialNumber()); - } - } else if (!screen->serialNumber().isEmpty()) { - item->m_model = screen->serialNumber(); - } - if (item->m_model.isEmpty()) - item->m_model = QStringLiteral("Unknown"); - - item->m_name = screen->name(); - item->m_description = screen->manufacturer() + QStringLiteral(" ") + screen->model(); - item->m_position = screen->availableGeometry().topLeft(); - item->m_physicalSize = screen->physicalSize(); - item->m_scaleFactor = qFloor(screen->devicePixelRatio()); - - QPlatformScreen::SubpixelAntialiasingType subpixel = screen->handle()->subpixelAntialiasingTypeHint(); - switch (subpixel) { - case QPlatformScreen::Subpixel_None: - item->m_subpixel = WaylandOutput::SubpixelNone; - break; - case QPlatformScreen::Subpixel_RGB: - item->m_subpixel = WaylandOutput::SubpixelHorizontalRgb; - break; - case QPlatformScreen::Subpixel_BGR: - item->m_subpixel = WaylandOutput::SubpixelHorizontalBgr; - break; - case QPlatformScreen::Subpixel_VRGB: - item->m_subpixel = WaylandOutput::SubpixelVerticalRgb; - break; - case QPlatformScreen::Subpixel_VBGR: - item->m_subpixel = WaylandOutput::SubpixelVerticalBgr; - break; - } - - switch (screen->orientation()) { - case Qt::PortraitOrientation: - item->m_transform = WaylandOutput::Transform90; - break; - case Qt::InvertedLandscapeOrientation: - item->m_transform = WaylandOutput::Transform180; - break; - case Qt::InvertedPortraitOrientation: - item->m_transform = WaylandOutput::Transform270; - break; - default: - break; - } - - if (screen->handle()->modes().count() == 0) { - ScreenMode *mode = new ScreenMode(item); - mode->m_resolution = screen->geometry().size(); - mode->m_refreshRate = qFloor(screen->refreshRate() * 1000); - item->m_modes.append(mode); - item->m_currentMode = mode; - item->m_preferredMode = mode; - } else { - int modeIndex = 0; - const auto modes = screen->handle()->modes(); - for (const auto &platformMode : modes) { - const QSize size = platformMode.size; - const int refresh = qFloor(platformMode.refreshRate * 1000); - - // Don't add the same modes twice - auto result = std::find_if(item->m_modes.begin(), item->m_modes.end(), [size, refresh](ScreenMode *mode) { - return mode->resolution() == size && mode->refreshRate() == refresh; - }); - if (result != item->m_modes.end()) - continue; - - ScreenMode *mode = new ScreenMode(item); - mode->m_resolution = size; - mode->m_refreshRate = refresh; - item->m_modes.append(mode); - - if (screen->handle()->currentMode() == modeIndex) - item->m_currentMode = mode; - if (screen->handle()->preferredMode() == modeIndex) - item->m_preferredMode = mode; - - modeIndex++; - } - } - - connect(screen, &QScreen::availableGeometryChanged, this, [this, item](const QRect &geometry) { - int row = m_items.indexOf(item); - - if (item->m_position != geometry.topLeft()) { - item->m_position = geometry.topLeft(); - Q_EMIT item->positionChanged(); - } - - for (auto *mode : qAsConst(item->m_modes)) { - const QSize size = geometry.size(); - const int refreshRate = qFloor(item->screen()->refreshRate() * 1000); - if (mode->resolution() == size && mode->refreshRate() == refreshRate) { - if (item->m_currentMode != mode) { - item->m_currentMode = mode; - Q_EMIT item->currentModeChanged(size, refreshRate); - } - break; - } - } - - Q_EMIT dataChanged(index(row, 0), index(row, 0)); - }); - connect(screen, &QScreen::refreshRateChanged, this, [this, item](qreal rr) { - int row = m_items.indexOf(item); - - for (auto *mode : qAsConst(item->m_modes)) { - const QSize size = item->screen()->availableGeometry().size(); - const int refreshRate = qFloor(rr * 1000); - if (mode->resolution() == size && mode->refreshRate() == refreshRate) { - if (item->m_currentMode != mode) { - item->m_currentMode = mode; - Q_EMIT item->currentModeChanged(size, refreshRate); - } - break; - } - } - - Q_EMIT dataChanged(index(row, 0), index(row, 0)); - }); - connect(screen, &QScreen::physicalSizeChanged, this, [this, item](const QSizeF &size) { - int row = m_items.indexOf(item); - item->m_physicalSize = size; - Q_EMIT item->physicalSizeChanged(); - Q_EMIT dataChanged(index(row, 0), index(row, 0)); - }); - connect(screen, &QScreen::primaryOrientationChanged, this, [this, item](Qt::ScreenOrientation orientation) { - int row = m_items.indexOf(item); - switch (orientation) { - case Qt::PortraitOrientation: - item->m_transform = WaylandOutput::Transform90; - break; - case Qt::InvertedLandscapeOrientation: - item->m_transform = WaylandOutput::Transform180; - break; - case Qt::InvertedPortraitOrientation: - item->m_transform = WaylandOutput::Transform270; - break; - default: - break; - } - Q_EMIT item->transformChanged(); - Q_EMIT dataChanged(index(row, 0), index(row, 0)); - }); - - m_items.append(item); - - endInsertRows(); -} diff --git a/src/imports/compositor/screenmodel.h b/src/imports/compositor/screenmodel.h deleted file mode 100644 index 1dfacf3c..00000000 --- a/src/imports/compositor/screenmodel.h +++ /dev/null @@ -1,180 +0,0 @@ -// SPDX-FileCopyrightText: 2018 Pier Luigi Fiorini -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef SCREENMODEL_H -#define SCREENMODEL_H - -#include -#include -#include -#include -#include - -#include -#include - -using namespace Aurora::Compositor; - -class ScreenModel : public QAbstractListModel, public QQmlParserStatus -{ - Q_OBJECT - Q_PROPERTY(QString fileName READ fileName WRITE setFileName NOTIFY fileNameChanged) - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_INTERFACES(QQmlParserStatus) -public: - enum Roles { - ScreenItemRole = Qt::UserRole, - ScreenRole, - ScreenIndexRole - }; - Q_ENUM(Roles) - - explicit ScreenModel(QObject *parent = nullptr); - ~ScreenModel() override; - - QString fileName() const; - void setFileName(const QString &fileName); - - int count() const; - - Q_INVOKABLE class ScreenItem *get(int index) const; - - QHash roleNames() const override; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - - Q_INVOKABLE bool testConfiguration(Aurora::Compositor::WaylandWlrOutputConfigurationV1 *configuration); - Q_INVOKABLE void applyConfiguration(Aurora::Compositor::WaylandWlrOutputConfigurationV1 *configuration); - -Q_SIGNALS: - void fileNameChanged(); - void countChanged(); - -protected: - void classBegin() override; - void componentComplete() override; - -private: - bool m_initialized = false; - QString m_fileName; - QVector m_items; - - void addFakeScreens(); - -private Q_SLOTS: - void handleScreenAdded(QScreen *screen); -}; - -class ScreenMode : public QObject -{ - Q_OBJECT - Q_PROPERTY(QSize resolution READ resolution CONSTANT) - Q_PROPERTY(int refreshRate READ refreshRate CONSTANT) -public: - explicit ScreenMode(QObject *parent = nullptr); - - QSize resolution() const; - int refreshRate() const; - - bool operator==(const ScreenMode &other) const; - -private: - friend class ScreenModel; - - QSize m_resolution; - int m_refreshRate = 60; -}; - -QML_DECLARE_TYPE(ScreenMode) - -class ScreenItem : public QObject -{ - Q_OBJECT - Q_PROPERTY(QScreen *screen READ screen CONSTANT) - Q_PROPERTY(int screenIndex READ screenIndex CONSTANT) - Q_PROPERTY(bool enabled READ isEnabled NOTIFY enabledChanged) - Q_PROPERTY(QString manufacturer READ manufacturer CONSTANT) - Q_PROPERTY(QString model READ model CONSTANT) - Q_PROPERTY(QString name READ name CONSTANT) - Q_PROPERTY(QString description READ description CONSTANT) - Q_PROPERTY(QPoint position READ position NOTIFY positionChanged) - Q_PROPERTY(QSizeF physicalSize READ physicalSize NOTIFY physicalSizeChanged) - Q_PROPERTY(qreal scaleFactor READ scaleFactor NOTIFY scaleFactorChanged) - Q_PROPERTY(Aurora::Compositor::WaylandOutput::Subpixel subpixel READ subpixel CONSTANT) - Q_PROPERTY(Aurora::Compositor::WaylandOutput::Transform transform READ transform NOTIFY transformChanged) - Q_PROPERTY(QQmlListProperty modes READ modesList CONSTANT) - Q_PROPERTY(ScreenMode *currentMode READ currentMode NOTIFY currentModeChanged) - Q_PROPERTY(ScreenMode *preferredMode READ preferredMode CONSTANT) - Q_PROPERTY(PowerState powerState READ powerState WRITE setPowerState NOTIFY powerStateChanged) -public: - enum PowerState { - PowerStateOn, - PowerStateStandby, - PowerStateSuspend, - PowerStateOff - }; - Q_ENUM(PowerState) - - explicit ScreenItem(QObject *parent = nullptr); - ~ScreenItem(); - - QScreen *screen() const; - int screenIndex() const; - - bool isEnabled() const; - - QString manufacturer() const; - QString model() const; - QString name() const; - QString description() const; - - QPoint position() const; - - QSizeF physicalSize() const; - - qreal scaleFactor() const; - - Aurora::Compositor::WaylandOutput::Subpixel subpixel() const; - Aurora::Compositor::WaylandOutput::Transform transform() const; - - QVector modes() const; - QQmlListProperty modesList(); - - ScreenMode *currentMode() const; - ScreenMode *preferredMode() const; - - PowerState powerState() const; - void setPowerState(PowerState state); - -Q_SIGNALS: - void enabledChanged(); - void positionChanged(); - void physicalSizeChanged(); - void scaleFactorChanged(); - void transformChanged(); - void currentModeChanged(const QSize &resolution, int refreshRate); - void powerStateChanged(); - -private: - friend class ScreenModel; - - QScreen *m_screen = nullptr; - bool m_enabled = true; - QString m_manufacturer; - QString m_model; - QString m_name; - QString m_description; - QPoint m_position; - QSizeF m_physicalSize; - qreal m_scaleFactor = 1.0f; - Aurora::Compositor::WaylandOutput::Subpixel m_subpixel = Aurora::Compositor::WaylandOutput::SubpixelUnknown; - Aurora::Compositor::WaylandOutput::Transform m_transform = Aurora::Compositor::WaylandOutput::TransformNormal; - ScreenMode *m_currentMode = nullptr; - ScreenMode *m_preferredMode = nullptr; - QVector m_modes; -}; - -QML_DECLARE_TYPE(ScreenItem) - -#endif // SCREENMODEL_H diff --git a/src/imports/shell-private/CMakeLists.txt b/src/imports/shell-private/CMakeLists.txt index 85ebaa9c..0a7e7702 100644 --- a/src/imports/shell-private/CMakeLists.txt +++ b/src/imports/shell-private/CMakeLists.txt @@ -6,9 +6,6 @@ find_package(Wayland REQUIRED) if(NOT TARGET Liri::Xdg) find_package(Liri1Xdg REQUIRED) endif() -if(NOT TARGET Liri::AuroraPlatformHeaders) - find_package(Liri1AuroraPlatformHeaders REQUIRED) -endif() if(NOT TARGET Liri::AuroraCompositor) find_package(Liri1AuroraCompositor REQUIRED) endif() @@ -40,7 +37,6 @@ target_link_libraries(ShellPrivateQmlPlugin Qt6::Qml Qt6::Quick Liri::Xdg - Liri::AuroraPlatformHeaders Liri::AuroraCompositor ) diff --git a/src/imports/shell/PanelItem.qml b/src/imports/shell/PanelItem.qml index bc26ab14..76030f52 100644 --- a/src/imports/shell/PanelItem.qml +++ b/src/imports/shell/PanelItem.qml @@ -45,7 +45,7 @@ Rectangle { acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: { + onClicked: (mouse) => { if (mouse.button === Qt.RightButton) item.rightClicked(mouse.x, mouse.y); else diff --git a/src/lockscreen/CMakeLists.txt b/src/lockscreen/CMakeLists.txt index 22575c1c..b264a157 100644 --- a/src/lockscreen/CMakeLists.txt +++ b/src/lockscreen/CMakeLists.txt @@ -18,7 +18,7 @@ add_executable(LiriShellLockScreen ${SOURCES}) file(GLOB_RECURSE _qml_files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" CMAKE_CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/qml/*.qml") qt6_add_qml_module(LiriShellLockScreen - URI lockscreen + URI LiriShellLockscreen QML_FILES ${_qml_files} ) diff --git a/src/lockscreen/main.cpp b/src/lockscreen/main.cpp index 12137cf5..fd37ea5f 100644 --- a/src/lockscreen/main.cpp +++ b/src/lockscreen/main.cpp @@ -56,8 +56,10 @@ int main(int argc, char *argv[]) LIRISHELL_VERSION, LIRISHELL_VERSION, GIT_REV); // Create UI - QSharedPointer engine(new QQmlApplicationEngine); - engine->load(QStringLiteral("qrc:/qt/qml/lockscreen/qml/main.qml")); + QQmlApplicationEngine engine; + engine.loadFromModule("LiriShellLockscreen", "Main"); + if (engine.rootObjects().isEmpty()) + return 1; return app.exec(); } diff --git a/src/lockscreen/qml/main.qml b/src/lockscreen/qml/Main.qml similarity index 100% rename from src/lockscreen/qml/main.qml rename to src/lockscreen/qml/Main.qml