deepin-screensaver源码分析

本文解析了deepin-screensaver的工作原理和技术实现,包括其与xscreensaver的关系、源码片段解读、不同接入方式的比较等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

整体逻辑

在这里插入图片描述
xscreensaver程序会等待键盘和鼠标空闲一段时间,然后运行随机选择的图形演示。只要有任何鼠标或键盘活动,它就会关闭。这个程序可以锁定你的终端,以防止其他人使用它,虽然它的默认操作模式只是在你的屏幕上显示漂亮的图片时,它不使用。它还提供配置和控制您的显示器的节能功能。

Deepin-screensaver:常驻服务,负责接受x.org screensaver 的消息,比如Start,Preview 等DBus消息,当受到消息时,建立渲染窗口,并拉起当前用户所选屏保应用,并将渲染窗口window id传给应用,应用根据指定window id进行渲染。

源码片段
deepin-screensaver会根据屏幕个数建立windowmap,并实例化渲染窗口

void DBusScreenSaver::ensureWindowMap()
{
    if (!m_windowMap.isEmpty())
        return;
    connect(qGuiApp, &QGuiApplication::screenAdded, this, &DBusScreenSaver::onScreenAdded, Qt::UniqueConnection);
    connect(qGuiApp, &QGuiApplication::screenRemoved, this, &DBusScreenSaver::onScreenRemoved, Qt::UniqueConnection);
    for (QScreen *s : qGuiApp->screens()) {
        onScreenAdded(s);
    }
}

渲染窗口:


class ScreenSaverView : public QQuickView
{
    Q_OBJECT
public:
    explicit ScreenSaverView(QWindow *parent = nullptr);
    ~ScreenSaverView();
    bool start(const QString &filePath);
    void stop();
signals:
    void inputEvent(QEvent::Type type);
private:
    bool event(QEvent *event) override;
    QProcess *m_process = nullptr;
    friend class ScreenSaverWindow;
};

从代码可以看出,渲染窗口提供的是继承QQuickView,QQuickView可以直接加载QML文件,无需执行文件,从下面的拉屏保应用的代码也可以看出,屏保程序是可以直接加载qml文件。

拉起进程:

bool ScreenSaverView::start(const QString &filePath)
{
    stop();
    if (filePath.endsWith(".qml")) {
        setSource(QUrl(filePath));
    } else {
        if (!m_process) {
            m_process = new QProcess(this);
            m_process->setProcessChannelMode(QProcess::ForwardedChannels);
        }
        create();
        m_process->start(filePath, {"-window-id", QString::number(winId())}, QIODevice::ReadOnly);
        if (!m_process->waitForStarted(3000)) {
            qDebug() << "Failed on start:" << m_process->program() << ", error string:" << m_process->errorString();
            return false;
        }
    }
    return true;
}

到这里deepin-screensaver的任务就完成了,剩下的就需要屏保硬保根据deepin-screensaver传入的-window-id进行渲染。

屏保调试程序

deepin和UOS自带了二进制screensaver工具,可以直接通过命令行使用

➜  ~ deepin-screensaver -h
Usage: deepin-screensaver [options] [name]
 
Options:
  -d, --dbus     Register DBus service.
  -s, --start    Start screen saver.
  -h, --help     Displays this help.
  -v, --version  Displays version information.
 
Arguments:
  screensaer-name Use the screensaver application.

我们可以直接deep-screensaver your_app的方式加载自己的屏保,类似于windows预览功能。

屏保的接入方式

rcc文件

这种方法是基于Qt开发环境,将资源文件打包成app.rcc文件,放到/usr/lib/deepin-screensaver/resources目录下,deepin-screensaver程序则会自动加载该rcc文件,具体操作方式:如何开发一款属于自己的时尚屏保应用

通过ScreenHack框架接入

xscreensaver的作者提供了一套框架及一些屏保的demo供开发者参考,介入方式:screenhack

内嵌窗口

代码实现

#include <QGuiApplication>
#include <QApplication>
#include <QQuickView>
#include <QX11Info>
#include <QtX11Extras>
#include <QVariant>
#include <QCommandLineParser>
#include <X11/Xlib.h>
#include <QWindow>
#include <QDebug>
#include "instance.h"
 
int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
 
    QGuiApplication app(argc, argv);
 
    qmlRegisterType<Instance>("FrontEnd",1,0, "Instance");
 
    QCommandLineParser parser;
    QString windowId;
    QQuickView s;
    s.setSource(QUrl("qrc:/rectwindow.qml")); // 加载自己需要的文件
 
    XWindowAttributes xwa;
 
    parser.setApplicationDescription("Test helper");
    parser.addHelpOption();
    parser.addVersionOption();
    parser.addPositionalArgument("source", QCoreApplication::translate("main", "Screensaver for ukui-screensaver"));
    parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
    parser.addOptions({
            {{"r", "root"},
            QCoreApplication::translate("main", "show on root window")},
            {{"w", "window-id"},
            QCoreApplication::translate("main", "show on window."),
            QCoreApplication::translate("main", "window id")},
            });
    parser.process(app);
 
    bool onWindow = parser.isSet("window-id");
    bool onRoot = parser.isSet("root");
 
    double scale = 1;
    QScreen *screen = QApplication::primaryScreen();
    scale = screen->devicePixelRatio();
 
 
    if(onWindow){
        windowId = parser.value("window-id");
        WId wid =  windowId.toULong();
        QWindow* window = QWindow::fromWinId(wid);
        window->setProperty("_q_embedded_native_parent_handle",QVariant(wid));
        s.winId();
        s.setParent(window);
        XGetWindowAttributes (QX11Info::display(), wid, &xwa);
 
        //获取屏保所在屏幕对应的缩放比例。
        for(auto screen : QGuiApplication::screens())
        {
            QPoint pos(xwa.x,xwa.y);
            if(screen->geometry().contains(pos)){
                scale = screen->devicePixelRatio();
            }
        }
 
        s.resize(xwa.width/scale + 1,xwa.height/scale + 1);
        s.setX(0);
        s.setY(0);
        s.show();
    }
    else if(onRoot){
        WId wid = QX11Info::appRootWindow();
        QWindow* window = QWindow::fromWinId(wid);
        window->setProperty("_q_embedded_native_parent_handle",QVariant(wid));
        s.winId();
        s.setParent(window);
        XGetWindowAttributes (QX11Info::display(), wid, &xwa);
        qDebug()<<"xwa.width = "<<xwa.width<<"xwa.height = "<<xwa.height;
        s.resize(xwa.width/scale + 1,xwa.height/scale + 1);
        s.setX(0);
        s.setY(0);
        s.show();
    }
    else{
        s.resize(1366,768);
        s.show();
    }
 
    return app.exec();
}

这里根据自己的需要加载不同的qml文件即可。

接入方式对比

方式优点缺点
rcc文件直接编写QML文件
开发效率高
对屏保的支持程度好
无法调用c++代码
内嵌窗口与正常使用Qt开发差异不大
ScreenHack框架官方提供的框架,只需要关注于绘制细节开发效率低

综合评估,采用内嵌窗口的方式为最佳方案

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值