feat: 实现Apps服务、Launcher服务、Dock服务

*重写Apps服务,新服务分为org.deepin.daemon.DFWatcher1和org.deepin.daemon.ALRecorder1两个服务
*重写Launcher服务, 新服务名为org.deepin.dde.daemon.Launcher1
*重写Dock服务, 新服务名为org.deepin.dde.daemon.Dock1
*重写部分go-lib接口,保存在src/lib目录, 后续从项目中提出统一存放至开发库
*使用XCB库实现与XServer交互,存放在src/lib目录
*放弃依赖dde-qt-dbus-factory包, 将xml文件生成的静态编译代码存放在frameworkdbus目录

Log: 实现Apps服务、Launcher服务、Dock服务
Task: https://pms.uniontech.com/task-view-109315.html
Influence: 无
Change-Id: Ia9676060bfe81ce8d02c48972cc3d3cbaf665a31
This commit is contained in:
Li Xi
2022-04-24 14:52:13 +08:00
committed by weizhixiang
parent dd7d4737bf
commit 13a1cabda1
118 changed files with 18347 additions and 3 deletions

View File

@ -0,0 +1,257 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "alrecorder.h"
#include "dfwatcher.h"
#include <QDir>
#include <QDBusConnection>
#include <QDBusError>
#include <QtDebug>
#include <QCryptographicHash>
const QString userAppsCfgDir = QDir::homePath() + "/.config/deepin/dde-daemon/apps/";
AlRecorder::AlRecorder(DFWatcher *_watcher, QObject *parent)
: QObject (parent)
, watcher(_watcher)
, mutex(QMutex(QMutex::NonRecursive))
{
QDBusConnection con = QDBusConnection::sessionBus();
if (!con.registerService("org.deepin.daemon.AlRecorder1"))
{
qWarning() << "register service AlRecorder1 error:" << con.lastError().message();
return;
}
if (!con.registerObject("/org/deepin/daemon/AlRecorder1", this, QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals))
{
qWarning() << "register object AlRecorder1 error:" << con.lastError().message();
return;
}
connect(watcher, &DFWatcher::Event, this, &AlRecorder::onDFChanged, Qt::QueuedConnection);
Q_EMIT ServiceRestarted();
}
AlRecorder::~AlRecorder()
{
QDBusConnection::sessionBus().unregisterObject("/org/deepin/daemon/AlRecorder1");
}
// 获取未启动应用列表
QMap<QString, QStringList> AlRecorder::GetNew()
{
QMap<QString, QStringList> ret;
QMutexLocker locker(&mutex);
for (auto is = subRecoders.begin(); is != subRecoders.end(); is++) {
QStringList apps;
for (auto il = is.value().launchedMap.begin(); il != is.value().launchedMap.end(); il++) {
if (!il.value()) // 未启动应用
apps.push_back(il.key());
}
if (apps.size() > 0)
ret[is.key()] = apps;
}
return ret;
}
// 标记应用已启动状态
void AlRecorder::MarkLaunched(const QString &filePath)
{
if (!filePath.endsWith(".desktop"))
return;
QFileInfo info;
QMutexLocker locker(&mutex);
for (auto sri = subRecoders.begin(); sri != subRecoders.end(); sri++) {
if (!filePath.contains(sri.key()))
continue;
info.setFile(filePath);
QString name = info.baseName();
for (auto li = sri.value().launchedMap.begin(); li != sri.value().launchedMap.end(); li++) {
if (li.key() == name && !li.value()) {
li.value() = true;
goto end;
}
}
}
// 根据filePath匹配到唯一应用
end:
if (info.isDir()) {
saveStatusFile(info.absolutePath() + "/");
Q_EMIT Launched(filePath);
}
}
// 记录Launcher服务卸载应用信息, 终端卸载不会调用该函数, desktopFiles为绝对路径
void AlRecorder::UninstallHints(const QStringList &desktopFiles)
{
QMutexLocker locker(&mutex);
for (auto desktop : desktopFiles) {
for (auto sri = subRecoders.begin(); sri != subRecoders.end(); sri++) {
if (!desktop.contains(sri.key()))
continue;
QFileInfo info(desktop);
sri.value().uninstallMap[info.baseName()] = true;
}
}
}
// 监控目录
void AlRecorder::WatchDirs(const QStringList &dataDirs)
{
for (auto dirPath : dataDirs) {
if (subRecoders.contains(dirPath))
continue;
QDir dir(dirPath);
QStringList files = dir.entryList(QDir::Files);
for (auto &file : files)
file = dirPath + file;
// 监听目录和文件
watcher->addDir(dirPath);
if (files.size() > 0)
watcher->addPaths(files);
// 初始化对应目录和应用信息
initSubRecoder(dirPath);
}
}
// 初始化应用目录记录
void AlRecorder::initSubRecoder(const QString &dirPath)
{
subRecorder sub;
QByteArray encryText = QCryptographicHash::hash(dirPath.toLatin1(), QCryptographicHash::Md5);
QString statusFile = userAppsCfgDir + "launched-" + encryText.toHex() + ".csv";
// 读取App状态记录
QMap<QString, bool> launchedApp;
QFile file(statusFile);
if (file.open(QIODevice::ReadWrite | QIODevice::Text)) {
while (!file.atEnd()){
QString line(file.readLine());
QStringList strs = line.split(",");
if (strs.length() != 2)
continue;
if (strs[1].size() > 0 && strs[1][0] == 't')
launchedApp[strs[0]] = true;
else
launchedApp[strs[0]] = false;
}
file.close();
} else {
// 读取app desktop
QDir dir(dirPath);
QStringList files = dir.entryList(QDir::Files);
QStringList apps;
for (QString file : files) {
if (!file.endsWith(".desktop"))
continue;
int index = file.lastIndexOf(".");
file.truncate(index);
qInfo() << "entry =" << file;
apps.push_back(file);
}
// fallback 都打开过
for (auto app : apps)
launchedApp[app] = true;
}
sub.statusFile = statusFile;
sub.launchedMap = launchedApp;
subRecoders[dirPath] = sub;
}
void AlRecorder::onDFChanged(const QString &filePath, uint32_t op)
{
QFileInfo info(filePath);
QString dirPath = info.absolutePath() + "/";
QString name = info.baseName();
subRecorder &sub = subRecoders[dirPath];
QMap<QString, bool> &launchedMap = sub.launchedMap;
// 过滤文件, 仅保留.desktop类型
if (!filePath.endsWith(".desktop"))
return;
switch (op) {
case DFWatcher::event::Add:
qInfo() << "AlRecorder: Add " << filePath;
if (!launchedMap.contains(name)) {
bool launched = false;
if (sub.removedLaunchedMap.find(name) != sub.removedLaunchedMap.end()) {
launched = sub.removedLaunchedMap[name];
sub.removedLaunchedMap.remove(name);
}
launchedMap[name] = launched;
}
sub.uninstallMap.remove(name);
saveStatusFile(dirPath); // 刷新状态文件
break;
case DFWatcher::event::Del:
qInfo() << "AlRecorder: Del" << filePath;
if (launchedMap.contains(name)) {
if (!sub.uninstallMap.contains(name))
sub.removedLaunchedMap[name] = launchedMap[name];
launchedMap.remove(name);
}
saveStatusFile(dirPath); //刷新状态文件
break;
case DFWatcher::event::Mod:
break;
}
}
void AlRecorder::saveStatusFile(const QString &dirPath)
{
subRecorder sub = subRecoders[dirPath];
QString tmpFile = sub.statusFile + "_tmp";
QFile fp(tmpFile);
bool ok = false;
qInfo() << "saveStatusFile=" << dirPath << "create file=" << tmpFile;
if (fp.open(QIODevice::ReadWrite | QIODevice::Text)) {
QTextStream out(&fp);
out << "# " << dirPath << endl;
for (auto rl = sub.launchedMap.begin(); rl != sub.launchedMap.end(); rl++) {
out << rl.key() << "," << ((rl.value() == true) ? "t" : "f") << endl;
}
ok = true;
fp.close();
}
// 覆盖原文件
QFile::remove(sub.statusFile);
QFile::rename(tmpFile, sub.statusFile);
Q_EMIT StatusSaved(dirPath, sub.statusFile, ok);
}

View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ALRECORDER_H
#define ALRECORDER_H
#include <QObject>
#include <QMap>
#include <QMutex>
class DFWatcher;
// 记录应用状态信息
class AlRecorder: public QObject
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.deepin.daemon.AlRecorder1")
public:
struct subRecorder {
QString statusFile; // 应用目录状态文件
QMap<QString, bool> launchedMap; // 应用启动记录
QMap<QString, bool> removedLaunchedMap; // desktop文件卸载记录
QMap<QString, bool> uninstallMap; // 记录应用将被卸载状态
};
AlRecorder(DFWatcher *_watcher, QObject *parent = nullptr);
~AlRecorder();
Q_SIGNALS:
void Launched(const QString &file);
void StatusSaved(const QString &root, const QString &file, bool ok);
void ServiceRestarted();
private Q_SLOTS:
void onDFChanged(const QString &filePath, uint32_t op);
public Q_SLOTS:
QMap<QString, QStringList> GetNew();
void MarkLaunched(const QString &filePath);
void UninstallHints(const QStringList &desktopFiles);
void WatchDirs(const QStringList &dataDirs);
private:
void initSubRecoder(const QString &dirPath);
void saveStatusFile(const QString &dirPath);
QMap<QString,subRecorder> subRecoders; // 记录不同应用目录的文件状态
DFWatcher *watcher;
QMutex mutex;
};
#endif // ALRECODER_H

View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "appmanager.h"
#include "dfwatcher.h"
#include "alrecorder.h"
#include "basedir.h"
#include <QDebug>
#include <QDir>
AppManager::AppManager(QObject *parent)
: QObject(parent)
, watcher(new DFWatcher(this))
, recorder(new AlRecorder(watcher, this))
{
QStringList dataDirs;
dataDirs << BaseDir::userAppDir().c_str();
for (auto &dir : BaseDir::sysAppDirs())
dataDirs << dir.c_str();
recorder->WatchDirs(dataDirs); // 监控应用desktop
}
AppManager::~AppManager()
{
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef APPMANAGER_H
#define APPMANAGER_H
#include <QObject>
class DFWatcher;
class AlRecorder;
class AppManager: public QObject
{
Q_OBJECT
public:
explicit AppManager(QObject *parent = nullptr);
~AppManager();
private:
DFWatcher *watcher;
AlRecorder *recorder;
};
#endif // APPMANAGER_H

View File

@ -0,0 +1,132 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dfwatcher.h"
#include <QDir>
#include <QDBusConnection>
#include <QDBusError>
#include <QtDebug>
const QString dfSuffix = ".desktop";
const QString configSuffix = ".json";
DFWatcher::DFWatcher(QObject *parent)
: QObject (parent)
, watcher(new QFileSystemWatcher(this))
{
QDBusConnection con = QDBusConnection::sessionBus();
if (!con.registerService("org.deepin.daemon.DFWatcher1"))
{
qInfo() << "register service app1 error:" << con.lastError().message();
return;
}
if (!con.registerObject("/org/deepin/daemon/DFWatcher1", this, QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals))
{
qInfo() << "register object DFWatcher error:" << con.lastError().message();
return;
}
connect(watcher, &QFileSystemWatcher::fileChanged, this, &DFWatcher::fileUpdated, Qt::QueuedConnection);
connect(watcher, &QFileSystemWatcher::directoryChanged, this, &DFWatcher::directoryUpdated, Qt::QueuedConnection);
}
DFWatcher::~DFWatcher()
{
QDBusConnection::sessionBus().unregisterObject("/org/deepin/daemon/DFWatcher1");
}
void DFWatcher::addDir(const QString &path)
{
// 记录当前目录内容
qInfo() << "addDir :" << path;
const QDir dir(path);
dirContentMap[path] = dir.entryList(QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Files, QDir::DirsFirst);
watcher->addPath(path);
}
void DFWatcher::addPaths(const QStringList &paths)
{
watcher->addPaths(paths);
}
QStringList DFWatcher::files()
{
return watcher->files();
}
void DFWatcher::removePath(const QString &filepath)
{
watcher->removePath(filepath);
}
void DFWatcher::fileUpdated(const QString &filePath)
{
qInfo() << "event: modify filepath=" << filePath;
if (filePath.endsWith(dfSuffix) || filePath.endsWith(configSuffix))
Q_EMIT Event(filePath, int(event::Mod));
}
void DFWatcher::directoryUpdated(const QString &dirPath)
{
QStringList recoderedContent = dirContentMap[dirPath];
const QDir dir(dirPath);
QStringList newEntries = dir.entryList(QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Files, QDir::DirsFirst);
QSet<QString> newDirSet = QSet<QString>::fromList(newEntries);
QSet<QString> currentDirSet = QSet<QString>::fromList(recoderedContent);
// 添加的文件
QSet<QString> newFiles = newDirSet - currentDirSet;
QStringList newFile = newFiles.toList();
// 移除的文件
QSet<QString> deletedFiles = currentDirSet - newDirSet;
QStringList deleteFile = deletedFiles.toList();
// 更新目录记录
dirContentMap[dirPath] = newEntries;
// 新增文件
if (newFile.size() > 0) {
for (auto &file : newFile) {
//qInfo() << "event: add filepath=" << file;
if (file.endsWith(dfSuffix)) {
Q_EMIT Event(dirPath + file, event::Add);
}
}
}
// 移除文件
if (deleteFile.size() > 0) {
for (auto &file : deleteFile) {
//qInfo() << "event: del filepath=" << file;
if (file.endsWith(dfSuffix)) {
Q_EMIT Event(dirPath + file, event::Del);
}
}
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DFWATCHER_H
#define DFWATCHER_H
#include <QObject>
#include <QMap>
#include <QFileSystemWatcher>
class DFWatcher: public QObject {
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.deepin.daemon.DFWatcher1")
public:
enum event{
Add,
Del,
Mod,
};
explicit DFWatcher(QObject *parent = nullptr);
~DFWatcher();
void addDir(const QString & path);
void addPaths(const QStringList &paths);
QStringList files();
void removePath(const QString &filepath);
Q_SIGNALS:
void Event(const QString &filepath, int op);
private Q_SLOTS:
void fileUpdated(const QString &filePath);
void directoryUpdated(const QString &dirPath);
private:
QFileSystemWatcher *watcher;
QMap<QString, QStringList> dirContentMap; // 监控的目录内容列表
};
#endif

View File

@ -0,0 +1,75 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "appinfo.h"
#include "common.h"
#include <QDebug>
#include <QString>
#include <QCryptographicHash>
AppInfo::AppInfo(DesktopInfo &info)
: isValid(true)
{
init(info);
}
AppInfo::AppInfo(const QString &_fileName)
: isValid(true)
{
DesktopInfo info(_fileName.toStdString());
init(info);
}
void AppInfo::init(DesktopInfo &info)
{
if (!info.isValidDesktop()) {
isValid = false;
return;
}
std::string xDeepinVendor= info.kf.getStr(MainSection, "X-Deepin-Vendor");
if (xDeepinVendor == "deepin") {
name = info.getGenericName().c_str();
if (name.isEmpty())
name = info.getName().c_str();
} else {
name = info.getName().c_str();
}
innerId = genInnerIdWithDesktopInfo(info);
fileName = info.getFileName().c_str();
id = info.getId().c_str();
icon = info.getIcon().c_str();
installed = info.isInstalled();
for (const auto & action : info.getActions()) {
actions.push_back(action);
}
}
QString AppInfo::genInnerIdWithDesktopInfo(DesktopInfo &info)
{
std::string cmdline = info.getCommandLine();
QByteArray encryText = QCryptographicHash::hash(QString(cmdline.c_str()).toLatin1(), QCryptographicHash::Md5);
QString innerId = desktopHashPrefix + encryText.toHex();
qInfo() << "app " << info.getId().c_str() << " generate innerId :" << innerId;
return innerId;
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef APPINFO_H
#define APPINFO_H
#include "desktopinfo.h"
#include<QVector>
// 应用信息类
class AppInfo
{
public:
explicit AppInfo(DesktopInfo &info);
explicit AppInfo(const QString &_fileName);
void init(DesktopInfo &info);
QString getFileName() {return fileName;}
QString getIcon() {return icon;}
QString getId() {return id;}
QString getInnerId() {return innerId;}
QString getName() {return name;}
QVector<DesktopAction> getActions() {return actions;}
QString getIdentifyMethod() {return identifyMethod;}
void setIdentifyMethod(QString method) {identifyMethod = method;}
bool isInstalled() {return installed;}
bool isValidApp() {return isValid;}
private:
QString genInnerIdWithDesktopInfo(DesktopInfo &info);
QString fileName;
QString id;
QString icon;
QString identifyMethod;
QString innerId;
QString name;
QVector<DesktopAction> actions;
bool installed;
bool isValid;
};
#endif // APPINFO_H

View File

@ -0,0 +1,91 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "appmenu.h"
#include <QJsonArray>
#include <QJsonDocument>
AppMenu::AppMenu()
: checkableMenu(false)
, singleCheck(false)
, itemcount(0)
, dirty(false)
{
}
// 增加菜单选项
void AppMenu::appendItem(AppMenuItem item)
{
if (!item.text.isEmpty()) {
item.id = allocateId();
items.push_back(item);
}
}
void AppMenu::handleAction(uint32_t timestamp, QString itemId)
{
for (auto &item : items) {
if (item.id == itemId) {
item.action(timestamp);
break;
}
}
}
void AppMenu::setDirtyStatus(bool isDirty)
{
dirty = isDirty;
}
QString AppMenu::getMenuJsonStr()
{
QJsonObject obj;
QJsonArray array;
for (auto item : items) {
QJsonObject objItem;
objItem["itemId"] = item.id;
objItem["itemText"] = item.text;
objItem["isActive"] = item.isActive;
objItem["isCheckable"] = item.isCheckable;
objItem["checked"] = item.checked;
objItem["itemIcon"] = item.icon;
objItem["itemIconHover"] = item.iconHover;
objItem["itemIconInactive"] = item.iconInactive;
objItem["showCheckMark"] = item.showCheckMark;
objItem["itemSubMenu"] = item.subMenu ? item.subMenu->getMenuJsonStr() : "";
array.push_back(QJsonValue(objItem));
}
obj["items"] = QJsonValue(array);
obj["checkableMenu"] = checkableMenu;
obj["singleCheck"] = singleCheck;
QString ret = QJsonDocument(obj).toJson();
return ret;
}
QString AppMenu::allocateId()
{
return QString::number(itemcount++);
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef APPMENU_H
#define APPMENU_H
#include <QString>
#include <QJsonObject>
#include <QVector>
#include <memory>
#include <vector>
#include <functional>
typedef std::function<void(uint32_t)> AppMenuAction;
class AppMenu;
// 应用菜单选项
struct AppMenuItem {
QString id;
QString text;
bool isActive;
QString isCheckable;
QString checked;
QString icon;
QString iconHover;
QString iconInactive;
QString showCheckMark;
std::shared_ptr<AppMenu> subMenu;
int hint;
AppMenuAction action;
};
// 应用菜单类
class AppMenu
{
public:
AppMenu();
void appendItem(AppMenuItem item);
void handleAction(uint32_t timestamp, QString itemId);
void setDirtyStatus(bool isDirty);
QString getMenuJsonStr();
private:
QString allocateId();
QVector<AppMenuItem> items; // json:"items"
bool checkableMenu; // json:"checkableMenu"
bool singleCheck; // json:"singleCheck"
int itemcount;
bool dirty;
};
#endif // APPMENU_H

103
src/modules/dock/common.h Normal file
View File

@ -0,0 +1,103 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COMMON_H
#define COMMON_H
#include <QString>
#include <QMap>
#include <QDir>
const QString configDock = "com.deepin.dde.dock";
const QString configAppearance = "com.deepin.dde.appearance";
const QString keyHideMode = "Hide_Mode";
const QString keyDisplayMode = "Display_Mode";
const QString keyPosition = "Position";
const QString keyIconSize = "Icon_Size";
const QString keyDockedApps = "Docked_Apps";
const QString keyShowTimeout = "Show_Timeout";
const QString keyHideTimeout = "Hide_Timeout";
const QString keyWindowSizeFashion = "Window_Size_Fashion";
const QString keyWindowSizeEfficient = "Window_Size_Efficient";
const QString keyWinIconPreferredApps = "Win_Icon_Preferred_Apps";
const QString keyOpacity = "Opacity";
const QString keyPluginSettings = "Plugin_Settings";
const QString keyForceQuitApp = "Force_Quit_App";
const QString scratchDir = QDir::homePath() + "/.local/dock/scratch/";
const QMap<QString, QString> pathDirCodeMap {
{"/usr/share/applications/", "/S@"},
{"/usr/local/share/applications/", "/L@"},
{QDir::homePath() + "/.local/share/applications", "/H@"},
{QDir::homePath() + "/.local/dock/scratch", "/D@"},
};
const QMap<QString, QString> pathCodeDirMap {
{"/S@", "/usr/share/applications/"},
{"/L@", "/usr/local/share/applications/"},
{"/H@", QDir::homePath() + "/.local/share/applications"},
{"/D@", QDir::homePath() + "/.local/dock/scratch"},
};
// DBus服务、路径
const QString dbusService = "org.deepin.dde.daemon.Dock1";
const QString dbusPath = "/org/deepin/dde/daemon/Dock1";
const QString dbusInterface = dbusService;
const QString windowPatternsFile = "/usr/share/dde/data/window_patterns.json";
const QString desktopHashPrefix = "d:";
const QString windowHashPrefix = "w:";
const QString entryDBusObjPathPrefix = dbusPath + "/entries/";
const QString entryDBusInterface = dbusInterface + ".Entry";
// 驻留应用desktop file模板
const QString dockedItemTemplate = R"([Desktop Entry]
Name=%1
Exec=%2
Icon=%3
Type=Application
Terminal=false
StartupNotify=false
)";
const QString frontendWindowWmClass = "dde-dock";
const int configureNotifyDelay = 100;
const int smartHideTimerDelay = 400;
const int bestIconSize = 48;
const int menuItemHintShowAllWindows = 1;
const int MotifHintFunctions = 1;
const int MotifHintDecorations = 2;
const int MotifHintInputMode = 4;
const int MotifHintStatus = 8;
const int MotifFunctionNone = 0;
const int MotifFunctionAll = 1;
const int MotifFunctionResize = 2;
const int MotifFunctionMove = 4;
const int MotifFunctionMinimize = 8;
const int MotifFunctionMaximize = 16;
const int MotifFunctionClose = 32;
const QString ddeLauncherWMClass = "dde-launcher";
#endif // COMMON_H

View File

@ -0,0 +1,229 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dbusadaptordock.h"
DBusAdaptorDock::DBusAdaptorDock(QObject *parent)
: QDBusAbstractAdaptor(parent)
{
// constructor
setAutoRelaySignals(true);
Dock *dock = static_cast<Dock *>(QObject::parent());
if (dock) {
connect(dock, &Dock::serviceRestarted, this, [&] {
Q_EMIT ServiceRestarted();});
connect(dock, &Dock::entryAdded, this, [&](QString entryObjPath, int32_t index){
Q_EMIT EntryAdded(QDBusObjectPath(entryObjPath), index);});
connect(dock, &Dock::entryRemoved, this, &DBusAdaptorDock::EntryRemoved);
connect(dock, &Dock::hideStateChanged, this, &DBusAdaptorDock::HideStateChanged);
connect(dock, &Dock::frontendWindowRectChanged, this, &DBusAdaptorDock::FrontendWindowRectChanged);
}
}
DBusAdaptorDock::~DBusAdaptorDock()
{
// destructor
}
int DBusAdaptorDock::displayMode() const
{
return parent()->getDisplayMode();
}
void DBusAdaptorDock::setDisplayMode(int value)
{
if (displayMode() != value) {
parent()->setDisplayMode(value);
Q_EMIT DisplayModeChanged();
}
}
QStringList DBusAdaptorDock::dockedApps() const
{
return parent()->getDockedApps();
}
QList<QDBusObjectPath> DBusAdaptorDock::entries() const
{
QList<QDBusObjectPath> ret;
for (auto path : parent()->getEntryPaths())
ret.push_back(QDBusObjectPath(path));
return ret;
}
int DBusAdaptorDock::hideMode() const
{
return int(parent()->getHideMode());
}
void DBusAdaptorDock::setHideMode(int value)
{
if (hideMode() != value) {
parent()->setHideMode(HideMode(value));
Q_EMIT HideModeChanged();
}
}
int DBusAdaptorDock::hideState() const
{
return int(parent()->getHideState());
}
uint DBusAdaptorDock::hideTimeout() const
{
return parent()->getHideTimeout();
}
void DBusAdaptorDock::setHideTimeout(uint value)
{
if (hideTimeout() != value) {
parent()->setHideTimeout(value);
Q_EMIT HideTimeoutChanged();
}
}
uint DBusAdaptorDock::windowSizeEfficient() const
{
return parent()->getWindowSizeEfficient();
}
void DBusAdaptorDock::setWindowSizeEfficient(uint value)
{
if (windowSizeEfficient() != value) {
parent()->setWindowSizeEfficient(value);
Q_EMIT WindowSizeEfficientChanged();
}
}
uint DBusAdaptorDock::windowSizeFashion() const
{
return parent()->getWindowSizeFashion();
}
void DBusAdaptorDock::setWindowSizeFashion(uint value)
{
if (windowSizeFashion() != value) {
parent()->setWindowSizeFashion(value);
Q_EMIT WindowSizeFashionChanged();
}
}
QRect DBusAdaptorDock::frontendWindowRect() const
{
return parent()->getFrontendWindowRect();
}
double DBusAdaptorDock::opacity() const
{
return parent()->getOpacity();
}
uint DBusAdaptorDock::iconSize() const
{
return parent()->getIconSize();
}
void DBusAdaptorDock::setIconSize(uint value)
{
if (iconSize() != value) {
parent()->setIconSize(value);
Q_EMIT IconSizeChanged();
}
}
int DBusAdaptorDock::position() const
{
return parent()->getPosition();
}
void DBusAdaptorDock::setPosition(int value)
{
if (position() != value) {
parent()->setPosition(value);
Q_EMIT PositionChanged();
}
}
uint DBusAdaptorDock::showTimeout() const
{
return parent()->getShowTimeout();
}
void DBusAdaptorDock::setShowTimeout(uint value)
{
if (showTimeout() != value) {
parent()->setShowTimeout(value);
Q_EMIT ShowTimeoutChanged();
}
}
Dock *DBusAdaptorDock::parent() const
{
return static_cast<Dock *>(QObject::parent());
}
void DBusAdaptorDock::CloseWindow(uint win)
{
parent()->closeWindow(win);
}
// for debug
QStringList DBusAdaptorDock::GetEntryIDs()
{
return parent()->getEntryIDs();
}
bool DBusAdaptorDock::IsDocked(const QString &desktopFile)
{
return parent()->isDocked(desktopFile);
}
bool DBusAdaptorDock::IsOnDock(const QString &desktopFile)
{
return parent()->isOnDock(desktopFile);
}
void DBusAdaptorDock::MoveEntry(int index, int newIndex)
{
parent()->moveEntry(index, newIndex);
}
QString DBusAdaptorDock::QueryWindowIdentifyMethod(uint win)
{
return parent()->queryWindowIdentifyMethod(win);
}
bool DBusAdaptorDock::RequestDock(const QString &desktopFile, int index)
{
return parent()->requestDock(desktopFile, index);
}
bool DBusAdaptorDock::RequestUndock(const QString &desktopFile)
{
return parent()->requestUndock(desktopFile);
}
void DBusAdaptorDock::SetFrontendWindowRect(int x, int y, uint width, uint height)
{
parent()->setFrontendWindowRect(x, y, width, height);
}

View File

@ -0,0 +1,193 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DBUSADAPTORDOCK_H
#define DBUSADAPTORDOCK_H
#include "dock.h"
#include <QtCore/QObject>
#include <QtCore/QMetaObject>
#include <QtCore/QVariant>
#include <QtDBus/QtDBus>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include <QRect>
/*
* Adaptor class for interface org.deepin.dde.daemon.Dock1
*/
class DBusAdaptorDock: public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.deepin.dde.daemon.Dock1")
Q_CLASSINFO("D-Bus Introspection", ""
" <interface name=\"org.deepin.dde.daemon.Dock1\">\n"
" <method name=\"CloseWindow\">\n"
" <arg direction=\"in\" type=\"u\" name=\"win\"/>\n"
" </method>\n"
" <method name=\"GetEntryIDs\">\n"
" <arg direction=\"out\" type=\"as\" name=\"list\"/>\n"
" </method>\n"
" <method name=\"IsDocked\">\n"
" <arg direction=\"in\" type=\"s\" name=\"desktopFile\"/>\n"
" <arg direction=\"out\" type=\"b\" name=\"value\"/>\n"
" </method>\n"
" <method name=\"IsOnDock\">\n"
" <arg direction=\"in\" type=\"s\" name=\"desktopFile\"/>\n"
" <arg direction=\"out\" type=\"b\" name=\"value\"/>\n"
" </method>\n"
" <method name=\"MoveEntry\">\n"
" <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
" <arg direction=\"in\" type=\"i\" name=\"newIndex\"/>\n"
" </method>\n"
" <method name=\"QueryWindowIdentifyMethod\">\n"
" <arg direction=\"in\" type=\"u\" name=\"win\"/>\n"
" <arg direction=\"out\" type=\"s\" name=\"identifyMethod\"/>\n"
" </method>\n"
" <method name=\"RequestDock\">\n"
" <arg direction=\"in\" type=\"s\" name=\"desktopFile\"/>\n"
" <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
" <arg direction=\"out\" type=\"b\" name=\"ok\"/>\n"
" </method>\n"
" <method name=\"RequestUndock\">\n"
" <arg direction=\"in\" type=\"s\" name=\"desktopFile\"/>\n"
" <arg direction=\"out\" type=\"b\" name=\"ok\"/>\n"
" </method>\n"
" <method name=\"SetFrontendWindowRect\">\n"
" <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
" <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
" <arg direction=\"in\" type=\"u\" name=\"width\"/>\n"
" <arg direction=\"in\" type=\"u\" name=\"height\"/>\n"
" </method>\n"
" <signal name=\"ServiceRestarted\"/>\n"
" <signal name=\"EntryAdded\">\n"
" <arg type=\"o\" name=\"path\"/>\n"
" <arg type=\"i\" name=\"index\"/>\n"
" </signal>\n"
" <signal name=\"EntryRemoved\">\n"
" <arg type=\"s\" name=\"entryId\"/>\n"
" </signal>\n"
" <property access=\"readwrite\" type=\"u\" name=\"ShowTimeout\"/>\n"
" <property access=\"readwrite\" type=\"u\" name=\"HideTimeout\"/>\n"
" <property access=\"readwrite\" type=\"u\" name=\"WindowSizeEfficient\"/>\n"
" <property access=\"readwrite\" type=\"u\" name=\"WindowSizeFashion\"/>\n"
" <property access=\"read\" type=\"iiii\" name=\"FrontendWindowRect\"/>\n"
" <property access=\"read\" type=\"d\" name=\"Opacity\"/>\n"
" <property access=\"read\" type=\"ao\" name=\"Entries\"/>\n"
" <property access=\"readwrite\" type=\"i\" name=\"HideMode\"/>\n"
" <property access=\"readwrite\" type=\"i\" name=\"DisplayMode\"/>\n"
" <property access=\"read\" type=\"i\" name=\"HideState\"/>\n"
" <property access=\"readwrite\" type=\"i\" name=\"Position\"/>\n"
" <property access=\"readwrite\" type=\"u\" name=\"IconSize\"/>\n"
" <property access=\"read\" type=\"as\" name=\"DockedApps\"/>\n"
" </interface>\n"
"")
public:
DBusAdaptorDock(QObject *parent);
virtual ~DBusAdaptorDock();
public: // PROPERTIES
Q_PROPERTY(int DisplayMode READ displayMode WRITE setDisplayMode NOTIFY DisplayModeChanged)
int displayMode() const;
void setDisplayMode(int value);
Q_PROPERTY(QStringList DockedApps READ dockedApps NOTIFY DockedAppsChanged)
QStringList dockedApps() const;
Q_PROPERTY(QList<QDBusObjectPath> Entries READ entries NOTIFY EntriesChanged)
QList<QDBusObjectPath> entries() const;
Q_PROPERTY(int HideMode READ hideMode WRITE setHideMode NOTIFY HideModeChanged)
int hideMode() const;
void setHideMode(int value);
Q_PROPERTY(int HideState READ hideState NOTIFY HideStateChanged)
int hideState() const;
Q_PROPERTY(uint HideTimeout READ hideTimeout WRITE setHideTimeout NOTIFY HideTimeoutChanged)
uint hideTimeout() const;
void setHideTimeout(uint value);
Q_PROPERTY(uint WindowSizeEfficient READ windowSizeEfficient WRITE setWindowSizeEfficient NOTIFY WindowSizeEfficientChanged)
uint windowSizeEfficient() const;
void setWindowSizeEfficient(uint value);
Q_PROPERTY(uint WindowSizeFashion READ windowSizeFashion WRITE setWindowSizeFashion NOTIFY WindowSizeFashionChanged)
uint windowSizeFashion() const;
void setWindowSizeFashion(uint value);
Q_PROPERTY(QRect FrontendWindowRect READ frontendWindowRect NOTIFY FrontendWindowRectChanged)
QRect frontendWindowRect() const;
Q_PROPERTY(double Opacity READ opacity NOTIFY OpacityChanged)
double opacity() const;
Q_PROPERTY(uint IconSize READ iconSize WRITE setIconSize NOTIFY IconSizeChanged)
uint iconSize() const;
void setIconSize(uint value);
Q_PROPERTY(int Position READ position WRITE setPosition NOTIFY PositionChanged)
int position() const;
void setPosition(int value);
Q_PROPERTY(uint ShowTimeout READ showTimeout WRITE setShowTimeout NOTIFY ShowTimeoutChanged)
uint showTimeout() const;
void setShowTimeout(uint value);
Dock *parent() const;
public Q_SLOTS: // METHODS
void CloseWindow(uint win);
QStringList GetEntryIDs();
bool IsDocked(const QString &desktopFile);
bool IsOnDock(const QString &desktopFile);
void MoveEntry(int index, int newIndex);
QString QueryWindowIdentifyMethod(uint win);
bool RequestDock(const QString &desktopFile, int index);
bool RequestUndock(const QString &desktopFile);
void SetFrontendWindowRect(int x, int y, uint width, uint height);
Q_SIGNALS: // SIGNALS
void ServiceRestarted();
void EntryAdded(const QDBusObjectPath &path, int index);
void EntryRemoved(const QString &entryId);
void DisplayModeChanged();
void DockedAppsChanged();
void OpacityChanged();
void EntriesChanged();
void HideModeChanged();
void WindowSizeEfficientChanged();
void WindowSizeFashionChanged();
void HideStateChanged();
void FrontendWindowRectChanged();
void HideTimeoutChanged();
void IconSizeChanged();
void PositionChanged();
void ShowTimeoutChanged();
};
#endif

View File

@ -0,0 +1,177 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dbusadaptorentry.h"
DBusAdaptorEntry::DBusAdaptorEntry(QObject *parent)
: QDBusAbstractAdaptor(parent)
{
// constructor
setAutoRelaySignals(true);
//qDBusRegisterMetaType<ExportWindowInfo>();
Entry *entry = static_cast<Entry *>(QObject::parent());
if (entry) {
connect(entry, &Entry::isActiveChanged, this, &DBusAdaptorEntry::IsActiveChanged);
connect(entry, &Entry::isDockedChanged, this, &DBusAdaptorEntry::IsDockedChanged);
connect(entry, &Entry::menuChanged, this, &DBusAdaptorEntry::MenuChanged);
connect(entry, &Entry::iconChanged, this, &DBusAdaptorEntry::IconChanged);
connect(entry, &Entry::nameChanged, this, &DBusAdaptorEntry::NameChanged);
connect(entry, &Entry::desktopFileChanged, this, &DBusAdaptorEntry::DesktopFileChanged);
connect(entry, &Entry::currentWindowChanged, this, &DBusAdaptorEntry::CurrentWindowChanged);
}
}
DBusAdaptorEntry::~DBusAdaptorEntry()
{
// destructor
}
uint DBusAdaptorEntry::currentWindow() const
{
return parent()->getCurrentWindow();
}
QString DBusAdaptorEntry::desktopFile() const
{
return parent()->getDesktopFile();
}
QString DBusAdaptorEntry::icon() const
{
return parent()->getIcon();
}
QString DBusAdaptorEntry::id() const
{
return parent()->getId();
}
bool DBusAdaptorEntry::isActive() const
{
return parent()->getIsActive();
}
bool DBusAdaptorEntry::isDocked() const
{
return parent()->getIsDocked();
}
QString DBusAdaptorEntry::menu() const
{
return parent()->getMenu();
}
QString DBusAdaptorEntry::name() const
{
return parent()->getName();
}
/*
QList<ExportWindowInfo> DBusAdaptorEntry::windowInfos()
{
return parent()->getExportWindowInfos();
}
*/
Entry *DBusAdaptorEntry::parent() const
{
return static_cast<Entry *>(QObject::parent());
}
void DBusAdaptorEntry::Activate(uint timestamp)
{
parent()->active(timestamp);
}
void DBusAdaptorEntry::Check()
{
parent()->check();
}
void DBusAdaptorEntry::ForceQuit()
{
parent()->forceQuit();
}
QList<QVariant> DBusAdaptorEntry::GetAllowedCloseWindows()
{
auto ids = parent()->getAllowedClosedWindowIds();
QList<QVariant> ret;
for (auto id : ids)
ret.push_back(id);
return ret;
}
void DBusAdaptorEntry::HandleDragDrop(uint timestamp, const QStringList &files)
{
parent()->handleDragDrop(timestamp, files);
}
void DBusAdaptorEntry::HandleMenuItem(uint timestamp, const QString &id)
{
parent()->handleMenuItem(timestamp, id);
}
void DBusAdaptorEntry::NewInstance(uint timestamp)
{
parent()->newInstance(timestamp);
}
void DBusAdaptorEntry::PresentWindows()
{
parent()->presentWindows();
}
void DBusAdaptorEntry::RequestDock()
{
parent()->requestDock();
}
void DBusAdaptorEntry::RequestUndock()
{
parent()->requestUndock();
}
QDBusArgument &operator <<(QDBusArgument &argument, const ExportWindowInfo &info)
{
argument.beginStructure();
argument << info.xid << info.title << info.flash;
argument.endStructure();
return argument;
}
const QDBusArgument &operator >>(const QDBusArgument &argument, ExportWindowInfo &info)
{
argument.beginStructure();
argument >> info.xid >> info.title >> info.flash;
argument.endStructure();
return argument;
}
QDebug operator<<(QDebug deg, const ExportWindowInfo &info)
{
qDebug() << "xid: " << info.xid << " title:" << info.title << " flash:" << info.flash;
return deg;
}

View File

@ -0,0 +1,145 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DBUSADAPTORENTRY_H
#define DBUSADAPTORENTRY_H
#include "entry.h"
#include <QtCore/QObject>
#include <QtCore/QMetaObject>
#include <QtCore/QVariant>
#include <QtDBus/QtDBus>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include <DBusExtendedAbstractInterface>
Q_DECLARE_METATYPE(ExportWindowInfo)
QDBusArgument &operator <<(QDBusArgument &argument, const ExportWindowInfo &info);
const QDBusArgument &operator >>(const QDBusArgument &argument, ExportWindowInfo &info);
QDebug operator<<(QDebug deg, const ExportWindowInfo &info);
/*
* Adaptor class for interface org.deepin.dde.daemon.Dock1.Entry
*/
class DBusAdaptorEntry: public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.deepin.dde.daemon.Dock1.Entry")
Q_CLASSINFO("D-Bus Introspection", ""
" <interface name=\"org.deepin.dde.daemon.Dock1.Entry\">\n"
" <method name=\"Activate\">\n"
" <arg direction=\"in\" type=\"u\" name=\"timestamp\"/>\n"
" </method>\n"
" <method name=\"Check\"/>\n"
" <method name=\"ForceQuit\"/>\n"
" <method name=\"GetAllowedCloseWindows\">\n"
" <arg direction=\"out\" type=\"au\" name=\"windows\"/>\n"
" </method>\n"
" <method name=\"HandleDragDrop\">\n"
" <arg direction=\"in\" type=\"u\" name=\"timestamp\"/>\n"
" <arg direction=\"in\" type=\"as\" name=\"files\"/>\n"
" </method>\n"
" <method name=\"HandleMenuItem\">\n"
" <arg direction=\"in\" type=\"u\" name=\"timestamp\"/>\n"
" <arg direction=\"in\" type=\"s\" name=\"id\"/>\n"
" </method>\n"
" <method name=\"NewInstance\">\n"
" <arg direction=\"in\" type=\"u\" name=\"timestamp\"/>\n"
" </method>\n"
" <method name=\"PresentWindows\"/>\n"
" <method name=\"RequestDock\"/>\n"
" <method name=\"RequestUndock\"/>\n"
" <property access=\"read\" type=\"s\" name=\"Name\"/>\n"
" <property access=\"read\" type=\"s\" name=\"Icon\"/>\n"
" <property access=\"read\" type=\"s\" name=\"Id\"/>\n"
" <property access=\"read\" type=\"b\" name=\"IsActive\"/>\n"
" <property access=\"read\" type=\"u\" name=\"CurrentWindow\"/>\n"
" <property access=\"read\" type=\"b\" name=\"IsDocked\"/>\n"
" <property access=\"read\" type=\"s\" name=\"Menu\"/>\n"
" <property access=\"read\" type=\"s\" name=\"DesktopFile\"/>\n"
" <property access=\"read\" type=\"a(usb)\" name=\"WindowInfos\"/>\n"
" </interface>\n"
"")
public:
DBusAdaptorEntry(QObject *parent);
virtual ~DBusAdaptorEntry();
public: // PROPERTIES
Q_PROPERTY(uint CurrentWindow READ currentWindow NOTIFY CurrentWindowChanged)
uint currentWindow() const;
Q_PROPERTY(QString DesktopFile READ desktopFile NOTIFY DesktopFileChanged)
QString desktopFile() const;
Q_PROPERTY(QString Icon READ icon NOTIFY IconChanged)
QString icon() const;
Q_PROPERTY(QString Id READ id)
QString id() const;
Q_PROPERTY(bool IsActive READ isActive NOTIFY IsActiveChanged)
bool isActive() const;
Q_PROPERTY(bool IsDocked READ isDocked NOTIFY IsDockedChanged)
bool isDocked() const;
Q_PROPERTY(QString Menu READ menu NOTIFY MenuChanged)
QString menu() const;
Q_PROPERTY(QString Name READ name NOTIFY NameChanged)
QString name() const;
//Q_PROPERTY(QString WindowInfos READ windowInfos)
//QList<ExportWindowInfo> windowInfos();
Entry *parent() const;
public Q_SLOTS: // METHODS
void Activate(uint timestamp);
void Check();
void ForceQuit();
QList<QVariant> GetAllowedCloseWindows();
void HandleDragDrop(uint timestamp, const QStringList &files);
void HandleMenuItem(uint timestamp, const QString &id);
void NewInstance(uint timestamp);
void PresentWindows();
void RequestDock();
void RequestUndock();
Q_SIGNALS: // SIGNALS
void IsActiveChanged(bool value);
void IsDockedChanged(bool value);
void MenuChanged(QString value);
void IconChanged(QString value);
void NameChanged(QString value);
void DesktopFileChanged(QString value);
void CurrentWindowChanged(uint32_t value);
};
#endif

View File

@ -0,0 +1,214 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dbushandler.h"
#include "dock.h"
#include "windowinfok.h"
#include "dbusbamfapplication.h"
DBusHandler::DBusHandler(Dock *_dock, QObject *parent)
: QObject(parent)
, dock(_dock)
, session(QDBusConnection::sessionBus())
, launcherEnd(new LauncherBackEnd("org.deepin.dde.daemon.Launcher1", "/org/deepin/dde/daemon/Launcher1", session, this))
, launcherFront(new LauncherFront("org.deepin.dde.Launcher1", "/org/deepin/dde/Launcher1", session, this))
, wm(new com::deepin::WM("com.deepin.wm", "/com/deepin/wm", session, this))
, wmSwitcher(new com::deepin::WMSwitcher("com.deepin.wmWMSwitcher", "/com/deepin/WMSwitcher", session, this))
, kwaylandManager(nullptr)
, bamfMatcher(new org::ayatana::bamf::BamfMatcher("org.ayatana.bamf.matcher", "/org/ayatana/bamf/matcher", session, this))
{
// 关联org.deepin.dde.daemon.Launcher1事件 ItemChanged
connect(launcherEnd, &LauncherBackEnd::ItemChanged, this, &DBusHandler::handleLauncherItemChanged);
// 关联org.deepin.dde.Launcher1事件 VisibleChanged
connect(launcherFront, &LauncherFront::VisibleChanged, this, [&](bool visible) {
dock->setDdeLauncherVisible(visible);
dock->updateHideState(false);
});
// 关联com.deepin.WMSwitcher事件 WMChanged
connect(wmSwitcher, &__WMSwitcher::WMChanged, this, [&](QString name) {dock->setWMName(name);});
}
// 关联com.deepin.daemon.KWayland.WindowManager事件
void DBusHandler::listenWaylandWMSignals()
{
kwaylandManager = new com::deepin::daemon::kwayland::WindowManager("com.deepin.daemon.KWayland", "/com/deepin/daemon/KWayland/WindowManager", session, this);
// ActiveWindowchanged
connect(kwaylandManager, &__KwaylandManager::ActiveWindowChanged, this, &DBusHandler::handleWlActiveWindowchange);
// WindowCreated
connect(kwaylandManager, &__KwaylandManager::WindowCreated, this, [&] (const QString &ObjPath) {
dock->registerWindowWayland(ObjPath);
});
// WindowRemove
connect(kwaylandManager, &__KwaylandManager::WindowRemove, this, [&] (const QString &ObjPath) {
dock->unRegisterWindowWayland(ObjPath);
});
}
void DBusHandler::handleLauncherItemChanged(const QString &status, LauncherItemInfo itemInfo, qlonglong categoryID)
{
qInfo() << "handleLauncherItemChanged status:" << status << " Name:" << itemInfo.Name << " ID:" << itemInfo.ID;
if (status == "deleted") {
dock->handleLauncherItemDeleted(itemInfo.Path);
} else if (status == "created") {
// don't need to download to dock when app reinstall
} else if (status == "updated") {
dock->handleLauncherItemUpdated(itemInfo.Path);
}
}
QString DBusHandler::getCurrentWM()
{
return wmSwitcher->CurrentWM().value();
}
// TODO 扩展ApplicationManager Run接口允许带参数启动应用暂时调用StartManager接口
void DBusHandler::launchApp(uint32_t timestamp, QStringList files)
{
QDBusInterface interface = QDBusInterface("com.deepin.StartManager", "/com/deepin/StartManager", "com.deepin.StartManager");
interface.call("LaunchApp", files, timestamp);
}
void DBusHandler::launchAppAction(uint32_t timestamp, QString file, QString section)
{
QDBusInterface interface = QDBusInterface("com.deepin.StartManager", "/com/deepin/StartManager", "com.deepin.StartManager");
interface.call("LaunchAppAction", file, section, timestamp);
}
void DBusHandler::markAppLaunched(const QString &filePath)
{
QDBusInterface interface = QDBusInterface("org.deepin.daemon.AlRecorder1", "/org/deepin/daemon/AlRecorder1", "org.deepin.daemon.AlRecorder1");
interface.call("MarkLaunched", filePath);
}
bool DBusHandler::wlShowingDesktop()
{
bool ret = false;
if (kwaylandManager)
ret = kwaylandManager->IsShowingDesktop().value();
return ret;
}
uint DBusHandler::wlActiveWindow()
{
uint ret = 0;
if (kwaylandManager)
ret = kwaylandManager->ActiveWindow().value();
return ret;
}
void DBusHandler::handleWlActiveWindowchange()
{
uint activeWinInternalId = wlActiveWindow();
if (activeWinInternalId == 0)
return;
WindowInfoK *info = dock->handleActiveWindowChangedK(activeWinInternalId);
if (info->getXid() != 0) {
dock->handleActiveWindowChanged(info);
} else {
dock->updateHideState(false);
}
}
void DBusHandler::listenKWindowSignals(WindowInfoK *windowInfo)
{
PlasmaWindow *window = windowInfo->getPlasmaWindow();
if (!window)
return;
// Title changed
connect(window, &PlasmaWindow::TitleChanged, this, [&] {
windowInfo->updateTitle();
auto entry = dock->getEntryByWindowId(windowInfo->getXid());
if (!entry)
return;
if (entry->getCurrentWindowInfo() == windowInfo)
entry->updateName();
entry->updateWindowInfos();
});
// Icon changed
connect(window, &PlasmaWindow::IconChanged, this, [&] {
windowInfo->updateIcon();
auto entry = dock->getEntryByWindowId(windowInfo->getXid());
if (!entry)
return;
entry->updateIcon();
});
// DemandingAttention changed
connect(window, &PlasmaWindow::DemandsAttentionChanged, this, [&] {
windowInfo->updateDemandingAttention();
auto entry = dock->getEntryByWindowId(windowInfo->getXid());
if (!entry)
return;
entry->updateWindowInfos();
});
// Geometry changed
connect(window, &PlasmaWindow::GeometryChanged, this, [&] {
if (!windowInfo->updateGeometry())
return;
dock->handleWindowGeometryChanged();
});
}
PlasmaWindow *DBusHandler::createPlasmaWindow(QString objPath)
{
return new PlasmaWindow("com.deepin.daemon.KWayland.PlasmaWindow", objPath, session, this);
}
// 取消关联信号
void DBusHandler::removePlasmaWindowHandler(PlasmaWindow *window)
{
}
void DBusHandler::presentWindows(QList<uint> windows)
{
wm->PresentWindows(windows);
}
QString DBusHandler::getDesktopFromWindowByBamf(XWindow windowId)
{
QDBusPendingReply<QString> reply = bamfMatcher->ApplicationForXid(windowId);
if (!reply.isValid())
return "";
QString appObjPath = reply.value();
org::ayatana::bamf::BamfApplication bamfApp("org.ayatana.bamf.application", appObjPath, session, this);
if (bamfApp.isValid())
return bamfApp.DesktopFile();
return "";
}

View File

@ -0,0 +1,92 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DBUSHANDLER_H
#define DBUSHANDLER_H
#include "dbuslauncher.h"
#include "dbuslauncherfront.h"
#include "dbuswm.h"
#include "dbuswmswitcher.h"
#include "dbuskwaylandwindowmanager.h"
#include "windowinfok.h"
#include "dbusplasmawindow.h"
#include "dbusbamfmatcher.h"
#include <QObject>
#include <QDBusConnection>
#include <QDBusMessage>
class Dock;
// 处理DBus交互
class DBusHandler : public QObject
{
Q_OBJECT
public:
explicit DBusHandler(Dock *_dock, QObject *parent = nullptr);
/************************* KWayland/WindowManager ***************************/
void listenWaylandWMSignals();
bool wlShowingDesktop();
uint wlActiveWindow();
/************************* daemon.Launcher1 ***************************/
void handleLauncherItemChanged(const QString &status, LauncherItemInfo itemInfo, qlonglong categoryID);
/************************* WMSwitcher ***************************/
QString getCurrentWM();
/************************* StartManager ***************************/
void launchApp(uint32_t timestamp, QStringList files);
void launchAppAction(uint32_t timestamp, QString file, QString section);
/************************* AlRecorder1 ***************************/
void markAppLaunched(const QString &filePath);
/************************* KWayland.PlasmaWindow ***************************/
void listenKWindowSignals(WindowInfoK *windowInfo);
PlasmaWindow *createPlasmaWindow(QString objPath);
void removePlasmaWindowHandler(PlasmaWindow *window);
/************************* WM ***************************/
void presentWindows(QList<uint> windows);
/************************* bamf ***************************/
// XWindow -> desktopFile
QString getDesktopFromWindowByBamf(XWindow windowId);
private Q_SLOTS:
void handleWlActiveWindowchange();
private:
Dock *dock;
QDBusConnection session;
LauncherBackEnd *launcherEnd;
LauncherFront *launcherFront;
com::deepin::WM *wm;
com::deepin::WMSwitcher *wmSwitcher;
com::deepin::daemon::kwayland::WindowManager *kwaylandManager;
org::ayatana::bamf::BamfMatcher *bamfMatcher;
};
#endif // DBUSHANDLER_H

1045
src/modules/dock/dock.cpp Normal file

File diff suppressed because it is too large Load Diff

191
src/modules/dock/dock.h Normal file
View File

@ -0,0 +1,191 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DOCK_H
#define DOCK_H
#include "synmodule.h"
#include "docksettings.h"
#include "entries.h"
#include "dbusplasmawindow.h"
#include <QStringList>
#include <QTimer>
#include <QMutex>
class WindowIdentify;
class DBusHandler;
class WaylandManager;
class X11Manager;
class WindowInfoK;
class WindowInfoX;
// 任务栏
class Dock : public SynModule
{
Q_OBJECT
enum class HideState
{
Unknown,
Show,
Hide,
};
public:
explicit Dock(QObject *parent = nullptr);
~Dock();
bool dockEntry(Entry *entry);
void undockEntry(Entry *entry);
QString allocEntryId();
bool shouldShowOnDock(WindowInfoBase *info);
void setDdeLauncherVisible(bool visible);
QString getWMName();
void setWMName(QString name);
void setPropHideMode(HideState state);
void attachOrDetachWindow(WindowInfoBase *info);
void attachWindow(WindowInfoBase *info);
void detachWindow(WindowInfoBase *info);
void markAppLaunched(const QString &filePath);
void launchApp(uint32_t timestamp, QStringList files);
void launchAppAction(uint32_t timestamp, QString file, QString section);
bool is3DWM();
bool isWaylandEnv();
WindowInfoK *handleActiveWindowChangedK(uint activeWin);
void handleActiveWindowChanged(WindowInfoBase *info);
void saveDockedApps();
void removeAppEntry(Entry *entry);
void handleWindowGeometryChanged();
Entry *getEntryByWindowId(XWindow windowId);
QString getDesktopFromWindowByBamf(XWindow windowId);
void registerWindowWayland(const QString &objPath);
void unRegisterWindowWayland(const QString &objPath);
AppInfo *identifyWindow(WindowInfoBase *winInfo, QString &innerId);
void markAppLaunched(AppInfo *appInfo);
void deleteWindow(XWindow xid);
ForceQuitAppMode getForceQuitAppStatus();
QVector<QString> getWinIconPreferredApps();
void handleLauncherItemDeleted(QString itemPath);
void handleLauncherItemUpdated(QString itemPath);
double getOpacity();
QRect getFrontendWindowRect();
int getDisplayMode();
void setDisplayMode(int mode);
QStringList getDockedApps();
QStringList getEntryPaths();
HideMode getHideMode();
void setHideMode(HideMode mode);
HideState getHideState();
void setHideState(HideState state);
uint getHideTimeout();
void setHideTimeout(uint timeout);
uint getIconSize();
void setIconSize(uint size);
int getPosition();
void setPosition(int position);
uint getShowTimeout();
void setShowTimeout(uint timeout);
uint getWindowSizeEfficient();
void setWindowSizeEfficient(uint size);
uint getWindowSizeFashion();
void setWindowSizeFashion(uint size);
// 设置配置
void setSynConfig(QByteArray ba);
QByteArray getSyncConfig();
/******************************** dbus handler ****************************/
PlasmaWindow *createPlasmaWindow(QString objPath);
void listenKWindowSignals(WindowInfoK *windowInfo);
void removePlasmaWindowHandler(PlasmaWindow *window);
void presentWindows(QList<uint> windows);
HideMode getDockHideMode();
WindowInfoBase *getActiveWindow();
QList<XWindow> getClientList();
void closeWindow(XWindow windowId);
QStringList getEntryIDs();
void setFrontendWindowRect(int32_t x, int32_t y, uint width, uint height);
bool isDocked(const QString desktopFile);
bool requestDock(QString desktopFile, int index);
bool requestUndock(QString desktopFile);
void moveEntry(int oldIndex, int newIndex);
bool isOnDock(QString desktopFile);
QString queryWindowIdentifyMethod(XWindow windowId);
QStringList getDockedAppsDesktopFiles();
QString getPluginSettings();
void setPluginSettings(QString jsonStr);
void mergePluginSettings(QString jsonStr);
void removePluginSettings(QString pluginName, QStringList settingkeys);
void updateHideState(bool delay);
Q_SIGNALS:
void serviceRestarted();
void entryAdded(QString entryObjPath, int32_t index);
void entryRemoved(QString id);
void hideStateChanged();
void frontendWindowRectChanged();
private Q_SLOTS:
void smartHideModeTimerExpired();
private:
void initSettings();
void updateMenu();
void initEntries();
void initDockedApps();
void initClientList();
WindowInfoX *findWindowByXidX(XWindow xid);
WindowInfoK *findWindowByXidK(XWindow xid);
bool isWindowDockOverlapX(XWindow xid);
bool isWindowDockOverlapK(WindowInfoBase *info);
Entry *getDockedEntryByDesktopFile(const QString &desktopFile);
bool shouldHideOnSmartHideMode();
QVector<XWindow> getActiveWinGroup(XWindow xid);
WindowIdentify *windowIdentify; // 窗口识别
Entries *entries; // 所有应用实例
int entriesSum; // 累计打开的应用数量
bool ddeLauncherVisible; // 前端启动器是否可见
QString wmName; // 窗管名称
WaylandManager *waylandManager; // wayland窗口管理
X11Manager *x11Manager; // X11窗口管理
QList<XWindow> clientList; // 所有窗口
QRect frontendWindowRect; // 前端任务栏大小, 用于智能隐藏时判断窗口是否重合
HideState hideState; // 记录任务栏隐藏状态
QTimer *smartHideTimer; // 任务栏智能隐藏定时器
WindowInfoBase *activeWindow;// 记录当前活跃窗口信息
WindowInfoBase *activeWindowOld;// 记录前一个活跃窗口信息
bool isWayland; // 判断是否为wayland环境
ForceQuitAppMode forceQuitAppStatus; // 强制退出应用状态
DBusHandler *dbusHandler; // 处理dbus交互
QMutex windowOperateMutex; // 窗口合并或拆分锁
};
#endif // DOCK_H

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dockmanager.h"
#include "dock.h"
#include <QDBusConnection>
#include <QDebug>
#include <QDBusError>
DockManager::DockManager(QObject *parent)
: QObject(parent)
, dock(new Dock(this))
{
adaptor = new DBusAdaptorDock(dock);
QDBusConnection con = QDBusConnection::sessionBus();
if (!con.registerService(dbusService)) {
qWarning() << "register service Dock1 error:" << con.lastError().message();
return;
}
if (!con.registerObject(dbusPath, dock, QDBusConnection::ExportAdaptors))
{
qWarning() << "register object Dock1 error:" << con.lastError().message();
return;
}
}
DockManager::~DockManager()
{
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DOCKMANAGER_H
#define DOCKMANAGER_H
#include "dbusadaptordock.h"
#include <QObject>
class Dock;
// 任务栏管理类
class DockManager : public QObject
{
Q_OBJECT
public:
explicit DockManager(QObject *parent = nullptr);
~DockManager();
Q_SIGNALS:
public Q_SLOTS:
private:
Dock *dock;
DBusAdaptorDock *adaptor;
};
#endif // DOCKMANAGER_H

View File

@ -0,0 +1,354 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "docksettings.h"
#include "settings.h"
#include <DConfig>
#include <QDebug>
#include <QJsonObject>
#include <QJsonDocument>
DCORE_USE_NAMESPACE
static DConfig *dockSettings = Settings::ConfigPtr(configDock);
static DConfig *appearanceSettings = Settings::ConfigPtr(configAppearance);
DockSettings::DockSettings(QObject *parent)
: QObject (parent)
{
init();
}
void DockSettings::init()
{
// 绑定属性
if (dockSettings) {
connect(dockSettings, &DConfig::valueChanged, this, [&] (const QString &key) {
if (key == keyHideMode) {
Q_EMIT hideModeChanged(HideMode(dockSettings->value(key).toInt()));
} else if(key == keyDisplayMode) {
Q_EMIT displayModeChanged(DisplayMode(dockSettings->value(key).toInt()));
} else if (key == keyPosition) {
Q_EMIT positionModeChanged(PositonMode(dockSettings->value(key).toInt()));
} else if (key == keyForceQuitApp){
Q_EMIT forceQuitAppChanged(dockSettings->value(key).toBool());
}
});
}
}
HideMode DockSettings::getHideMode()
{
HideMode ret = HideMode::Unknown;
if (dockSettings) {
QString mode = dockSettings->value(keyHideMode).toString();
HideModeHandler handler(mode);
ret = handler.toEnum();
}
return ret;
}
void DockSettings::setHideMode(HideMode mode)
{
if (dockSettings) {
dockSettings->setValue(keyHideMode, HideModeHandler(mode).toString());
}
}
DisplayMode DockSettings::getDisplayMode()
{
DisplayMode ret = DisplayMode::Unknown;
if (dockSettings) {
QString mode = dockSettings->value(keyDisplayMode).toString();
DisplayModeHandler handler(mode);
ret = handler.toEnum();
}
return ret;
}
void DockSettings::setDisplayMode(DisplayMode mode)
{
if (dockSettings) {
dockSettings->setValue(keyHideMode, DisplayModeHandler(mode).toString());
}
}
PositonMode DockSettings::getPositionMode()
{
PositonMode ret = PositonMode::Unknown;
if (dockSettings) {
QString mode = dockSettings->value(keyPosition).toString();
PositonModeHandler handler(mode);
ret = handler.toEnum();
}
return ret;
}
void DockSettings::setPositionMode(PositonMode mode)
{
if (dockSettings) {
dockSettings->setValue(keyPosition, PositonModeHandler(mode).toString());
}
}
ForceQuitAppMode DockSettings::getForceQuitAppMode()
{
ForceQuitAppMode ret = ForceQuitAppMode::Unknown;
if (dockSettings) {
QString mode = dockSettings->value(keyForceQuitApp).toString();
ForceQuitAppModeHandler handler(mode);
ret = handler.toEnum();
}
return ret;
}
void DockSettings::setForceQuitAppMode(ForceQuitAppMode mode)
{
if (dockSettings) {
dockSettings->setValue(keyForceQuitApp, ForceQuitAppModeHandler(mode).toString());
}
}
uint DockSettings::getIconSize()
{
uint size = 36;
if (dockSettings) {
size = dockSettings->value(keyIconSize).toUInt();
}
return size;
}
void DockSettings::setIconSize(uint size)
{
if (dockSettings) {
dockSettings->setValue(keyIconSize, size);
}
}
uint DockSettings::getShowTimeout()
{
uint time = 100;
if (dockSettings) {
time = dockSettings->value(keyShowTimeout).toUInt();
}
return time;
}
void DockSettings::setShowTimeout(uint time)
{
if (dockSettings) {
dockSettings->setValue(keyShowTimeout, time);
}
}
uint DockSettings::getHideTimeout()
{
uint time = 0;
if (dockSettings) {
time = dockSettings->value(keyHideTimeout).toUInt();
}
return time;
}
void DockSettings::setHideTimeout(uint time)
{
if (dockSettings) {
dockSettings->setValue(keyHideTimeout, time);
}
}
uint DockSettings::getWindowSizeEfficient()
{
uint size = 40;
if (dockSettings) {
size = dockSettings->value(keyWindowSizeEfficient).toUInt();
}
return size;
}
void DockSettings::setWindowSizeEfficient(uint size)
{
if (dockSettings) {
dockSettings->setValue(keyWindowSizeEfficient, size);
}
}
uint DockSettings::getWindowSizeFashion()
{
uint size = 48;
if (dockSettings) {
size = dockSettings->value(keyWindowSizeFashion).toUInt();
}
return size;
}
void DockSettings::setWindowSizeFashion(uint size)
{
if (dockSettings) {
dockSettings->setValue(keyWindowSizeFashion, size);
}
}
QStringList DockSettings::getDockedApps()
{
QStringList ret;
if (!dockSettings)
return ret;
for(const auto &var : dockSettings->value(keyDockedApps).toList()) {
if (var.isValid())
ret.push_back(var.toString());
}
return ret;
}
void DockSettings::setDockedApps(QList<QString> &apps)
{
if (!dockSettings)
return;
qDebug() << "docked apps:" << apps;
QVariantList list;
for (auto app : apps) {
list << QVariant(app);
}
dockSettings->setValue(keyDockedApps, list);
}
double DockSettings::getOpacity()
{
double opacity = 0.4;
if (appearanceSettings) {
opacity = appearanceSettings->value(keyOpacity).toDouble();
}
return opacity;
}
QVector<QString> DockSettings::getWinIconPreferredApps()
{
QVector<QString> ret;
if (dockSettings) {
for(const auto &var : dockSettings->value(keyWinIconPreferredApps).toList()) {
if (var.isValid())
ret.push_back(var.toString());
}
}
return ret;
}
QString DockSettings::getPluginSettings()
{
QString ret;
if (dockSettings) {
ret = dockSettings->value(keyPluginSettings).toString();
}
qInfo() << "getpluginsettings:" << ret;
return ret;
}
void DockSettings::setPluginSettings(QString jsonStr)
{
if (jsonStr.isEmpty())
return;
if (dockSettings) {
dockSettings->setValue(keyPluginSettings, jsonStr);
}
}
void DockSettings::mergePluginSettings(QString jsonStr)
{
QString origin = getPluginSettings();
QJsonObject originSettings = plguinSettingsStrToObj(origin);
QJsonObject needMergeSettings = plguinSettingsStrToObj(jsonStr);
for (auto pluginsIt = needMergeSettings.begin(); pluginsIt != needMergeSettings.end(); pluginsIt++) {
const QString &pluginName = pluginsIt.key();
const QJsonObject &needMergeSettingsObj = pluginsIt.value().toObject();
QJsonObject originSettingsObj = originSettings.value(pluginName).toObject();
for (auto settingsIt = needMergeSettingsObj.begin(); settingsIt != needMergeSettingsObj.end(); settingsIt++) {
originSettingsObj.insert(settingsIt.key(), settingsIt.value());
}
// 重写plugin对应的设置
originSettings.remove(pluginName);
originSettings.insert(pluginName, originSettingsObj);
}
setPluginSettings(QJsonDocument(originSettings).toJson(QJsonDocument::JsonFormat::Compact));
}
void DockSettings::removePluginSettings(QString pluginName, QStringList settingkeys)
{
if (pluginName.isEmpty())
return;
QString origin = getPluginSettings();
QJsonObject originSettings = plguinSettingsStrToObj(origin);
if (settingkeys.size() == 0) {
originSettings.remove(pluginName);
} else {
for (auto pluginsIt = originSettings.begin(); pluginsIt != originSettings.end(); pluginsIt++) {
const QString &pluginName = pluginsIt.key();
if (pluginName != pluginName)
continue;
QJsonObject originSettingsObj = originSettings.value(pluginName).toObject();
for (const auto &key : settingkeys) {
originSettingsObj.remove(key);
}
// 重写plugin对应的设置
originSettings.remove(pluginName);
originSettings.insert(pluginName, originSettingsObj);
}
}
setPluginSettings(QJsonDocument(originSettings).toJson(QJsonDocument::JsonFormat::Compact));
}
// 借鉴自dde-dock
QJsonObject DockSettings::plguinSettingsStrToObj(QString jsonStr)
{
QJsonObject ret;
const QJsonObject &pluginSettingsObject = QJsonDocument::fromJson(jsonStr.toLocal8Bit()).object();
if (pluginSettingsObject.isEmpty()) {
return ret;
}
for (auto pluginsIt = pluginSettingsObject.constBegin(); pluginsIt != pluginSettingsObject.constEnd(); ++pluginsIt) {
const QString &pluginName = pluginsIt.key();
const QJsonObject &settingsObject = pluginsIt.value().toObject();
QJsonObject newSettingsObject = ret.value(pluginName).toObject();
for (auto settingsIt = settingsObject.constBegin(); settingsIt != settingsObject.constEnd(); ++settingsIt) {
newSettingsObject.insert(settingsIt.key(), settingsIt.value());
}
// TODO: remove not exists key-values
ret.insert(pluginName, newSettingsObject);
}
return ret;
}

View File

@ -0,0 +1,275 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DOCKSETTINGS_H
#define DOCKSETTINGS_H
#include "common.h"
#include <QObject>
// 隐藏模式
enum class HideMode {
KeepShowing,
KeepHidden,
SmartHide,
Unknown,
};
class HideModeHandler {
HideMode modeEnum;
QString modeStr;
public:
HideModeHandler(HideMode mode) : modeEnum(mode), modeStr("") {}
HideModeHandler(QString mode) : modeEnum(HideMode::Unknown), modeStr(mode) {}
bool equal(HideModeHandler hideMode) {
return toString() == hideMode.toString() || toEnum() == hideMode.toEnum();
}
QString toString() {
switch (modeEnum) {
case HideMode::KeepShowing:
return "keep-showing";
case HideMode::KeepHidden:
return "keep-hidden";
case HideMode::SmartHide:
return "smart-hide";
case HideMode::Unknown:
default:
return "unknown";
}
}
HideMode toEnum() {
if (modeStr == "keep-showing")
return HideMode::KeepHidden;
else if (modeStr == "keep-hidden")
return HideMode::KeepHidden;
else if (modeStr == "smart-hide")
return HideMode::SmartHide;
else
return HideMode::Unknown;
}
};
// 显示样式
enum class DisplayMode {
Fashion,
Efficient,
Unknown,
};
class DisplayModeHandler {
DisplayMode modeEnum;
QString modeStr;
public:
DisplayModeHandler(DisplayMode mode) : modeEnum(mode), modeStr("") {}
DisplayModeHandler(QString mode) : modeEnum(DisplayMode::Unknown), modeStr(mode) {}
bool equal(DisplayModeHandler displayMode) {
return toString() == displayMode.toString() || toEnum() == displayMode.toEnum();
}
QString toString() {
switch (modeEnum) {
case DisplayMode::Fashion:
return "fashion";
case DisplayMode::Efficient:
return "efficient";
case DisplayMode::Unknown:
default:
return "unknown";
}
}
DisplayMode toEnum() {
if (modeStr == "fashion")
return DisplayMode::Fashion;
else if (modeStr == "efficient")
return DisplayMode::Efficient;
else
return DisplayMode::Unknown;
}
};
// 显示位置
enum class PositonMode {
TOP,
Right,
Bottom,
Left,
Unknown,
};
class PositonModeHandler {
PositonMode modeEnum;
QString modeStr;
public:
PositonModeHandler(PositonMode mode) : modeEnum(mode), modeStr("") {}
PositonModeHandler(QString mode) : modeEnum(PositonMode::Unknown), modeStr(mode) {}
bool equal(PositonModeHandler displayMode) {
return toString() == displayMode.toString() || toEnum() == displayMode.toEnum();
}
QString toString() {
switch (modeEnum) {
case PositonMode::TOP:
return "top";
case PositonMode::Right:
return "right";
case PositonMode::Bottom:
return "bottom";
case PositonMode::Left:
return "left";
case PositonMode::Unknown:
default:
return "unknown";
}
}
PositonMode toEnum() {
if (modeStr == "top")
return PositonMode::TOP;
else if (modeStr == "right")
return PositonMode::Right;
else if (modeStr == "bottom")
return PositonMode::Bottom;
else if (modeStr == "left")
return PositonMode::Left;
else
return PositonMode::Unknown;
}
};
// 强制退出应用菜单状态
enum class ForceQuitAppMode {
Enabled, // 开启
Disabled, // 关闭
Deactivated, // 置灰
Unknown
};
class ForceQuitAppModeHandler {
ForceQuitAppMode modeEnum;
QString modeStr;
public:
ForceQuitAppModeHandler(ForceQuitAppMode mode) : modeEnum(mode), modeStr("") {}
ForceQuitAppModeHandler(QString mode) : modeEnum(ForceQuitAppMode::Unknown), modeStr(mode) {}
bool equal(ForceQuitAppModeHandler displayMode) {
return toString() == displayMode.toString() || toEnum() == displayMode.toEnum();
}
QString toString() {
switch (modeEnum) {
case ForceQuitAppMode::Enabled:
return "enabled";
case ForceQuitAppMode::Disabled:
return "disabled";
case ForceQuitAppMode::Deactivated:
return "deactivated";
case ForceQuitAppMode::Unknown:
default:
return "unknown";
}
}
ForceQuitAppMode toEnum() {
if (modeStr == "enabled")
return ForceQuitAppMode::Enabled;
else if (modeStr == "disabled")
return ForceQuitAppMode::Disabled;
else if (modeStr == "deactivated")
return ForceQuitAppMode::Deactivated;
else
return ForceQuitAppMode::Unknown;
}
};
class Settings;
// 任务栏组策略配置类
class DockSettings: public QObject
{
Q_OBJECT
public:
static inline DockSettings *instance() {
static DockSettings instance;
return &instance;
}
void init();
HideMode getHideMode();
void setHideMode(HideMode mode);
DisplayMode getDisplayMode();
void setDisplayMode(DisplayMode mode);
PositonMode getPositionMode();
void setPositionMode(PositonMode mode);
ForceQuitAppMode getForceQuitAppMode();
void setForceQuitAppMode(ForceQuitAppMode mode);
uint getIconSize();
void setIconSize(uint size);
uint getShowTimeout();
void setShowTimeout(uint time);
uint getHideTimeout();
void setHideTimeout(uint time);
uint getWindowSizeEfficient();
void setWindowSizeEfficient(uint size);
uint getWindowSizeFashion();
void setWindowSizeFashion(uint size);
QStringList getDockedApps();
void setDockedApps(QList<QString> &apps);
double getOpacity();
QVector<QString> getWinIconPreferredApps();
// plugin settings
QString getPluginSettings();
void setPluginSettings(QString jsonStr);
void mergePluginSettings(QString jsonStr);
void removePluginSettings(QString pluginName, QStringList settingkeys);
QJsonObject plguinSettingsStrToObj(QString jsonStr);
Q_SIGNALS:
// 隐藏模式改变
void hideModeChanged(HideMode mode);
// 显示样式改变
void displayModeChanged(DisplayMode mode);
// 显示位置改变
void positionModeChanged(PositonMode mode);
// 强制退出应用开关改变
void forceQuitAppChanged(bool forced);
private:
DockSettings(QObject *paret = nullptr);
DockSettings(const DockSettings &);
DockSettings& operator= (const DockSettings &);
};
#endif // DOCKSETTINGS_H

View File

@ -0,0 +1,215 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "entries.h"
#include "dock.h"
Entries::Entries(Dock *_dock)
: dock(_dock)
{
}
QVector<Entry *> Entries::filterDockedEntries()
{
QVector<Entry *> ret;
for (auto &entry : items) {
if (entry->isValid() && entry->getIsDocked())
ret.push_back(entry);
}
return ret;
}
Entry *Entries::getByInnerId(QString innerId)
{
Entry *ret = nullptr;
for (auto &entry : items) {
if (entry->getInnerId() == innerId)
ret = entry;
}
return ret;
}
void Entries::append(Entry *entry)
{
insert(entry, -1);
}
void Entries::insert(Entry *entry, int index)
{
if (index < 0 || index >= items.size()) {
// append
items.push_back(entry);
} else {
// insert
items.insert(index, entry);
}
insertCb(entry, index);
}
void Entries::remove(Entry *entry)
{
for (auto iter = items.begin(); iter != items.end(); iter++) {
if ((*iter)->getId() == entry->getId()) {
items.erase(iter);
removeCb(entry);
delete entry;
}
}
}
void Entries::move(int oldIndex, int newIndex)
{
if (oldIndex == newIndex || oldIndex < 0 || newIndex < 0 || oldIndex >= items.size() || newIndex >= items.size())
return;
items.swap(oldIndex, newIndex);
}
Entry *Entries::getByWindowPid(int pid)
{
Entry *ret = nullptr;
for (auto &entry : items) {
if (entry->getWindowInfoByPid(pid)) {
ret = entry;
break;
}
}
return ret;
}
Entry *Entries::getByWindowId(XWindow windowId)
{
Entry *ret = nullptr;
for (auto &entry : items) {
if (entry->getWindowInfoByWinId(windowId)) {
ret = entry;
break;
}
}
return ret;
}
Entry *Entries::getByDesktopFilePath(QString filePath)
{
Entry *ret = nullptr;
for (auto &entry : items) {
if (entry->getFileName() == filePath) {
ret = entry;
break;
}
}
return ret;
}
QStringList Entries::getEntryIDs()
{
QStringList list;
for (auto item : items)
list.push_back(item->getId());
return list;
}
Entry *Entries::getDockedEntryByDesktopFile(const QString &desktopFile)
{
Entry *ret = nullptr;
for (auto entry : filterDockedEntries()) {
if (!entry->isValid())
continue;
if (desktopFile == entry->getFileName()) {
ret = entry;
break;
}
}
return ret;
}
QString Entries::queryWindowIdentifyMethod(XWindow windowId)
{
QString ret;
for (auto entry : items) {
auto window = entry->getWindowInfoByWinId(windowId);
if (window) {
auto app = window->getAppInfo();
if (app) {
ret = app->getIdentifyMethod();
} else {
ret = "Failed";
}
break;
}
}
return ret;
}
void Entries::handleActiveWindowChanged(XWindow activeWindId)
{
for (auto entry : items) {
auto windowInfo = entry->getWindowInfoByWinId(activeWindId);
if (windowInfo) {
entry->setPropIsActive(true);
entry->setCurrentWindowInfo(windowInfo);
entry->updateName();
entry->updateIcon();
} else {
entry->setPropIsActive(false);
}
}
}
void Entries::deleteWindow(XWindow xid)
{
for (auto entry : items) {
if (entry->containsWindow(xid)) {
entry->deleteWindow(xid);
break;
}
}
}
void Entries::updateEntriesMenu()
{
for (auto entry : items) {
entry->updateMenu();
}
}
void Entries::insertCb(Entry *entry, int index)
{
QString objPath = entryDBusObjPathPrefix + entry->getId();
Q_EMIT dock->entryAdded(objPath, index);
}
void Entries::removeCb(Entry *entry)
{
Q_EMIT dock->entryRemoved(entry->getId());
entry->stopExport();
}

View File

@ -0,0 +1,60 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ENTRIES_H
#define ENTRIES_H
#include "entry.h"
#include <QVector>
#include <QWeakPointer>
class Dock;
// 所有应用管理类
class Entries
{
public:
Entries(Dock *_dock);
QVector<Entry *> filterDockedEntries();
Entry *getByInnerId(QString innerId);
void append(Entry *entry);
void insert(Entry *entry, int index);
void remove(Entry *entry);
void move(int oldIndex, int newIndex);
Entry *getByWindowPid(int pid);
Entry *getByWindowId(XWindow windowId);
Entry *getByDesktopFilePath(QString filePath);
QStringList getEntryIDs();
Entry *getDockedEntryByDesktopFile(const QString &desktopFile);
QString queryWindowIdentifyMethod(XWindow windowId);
void handleActiveWindowChanged(XWindow activeWindId);
void deleteWindow(XWindow xid);
void updateEntriesMenu();
void insertCb(Entry *entry, int index);
void removeCb(Entry *entry);
private:
QList<Entry *> items;
Dock *dock;
};
#endif // ENTRIES_H

787
src/modules/dock/entry.cpp Normal file
View File

@ -0,0 +1,787 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "entry.h"
#include "dock.h"
#include "processinfo.h"
#include "dbusadaptorentry.h"
#include <QDebug>
#include <QDBusInterface>
#include<signal.h>
Entry::Entry(Dock *_dock, AppInfo *_app, QString _innerId, QObject *parent)
: QObject(parent)
, dock(_dock)
, app(nullptr)
, menu(nullptr)
, isActive(false)
, isDocked(false)
, innerId(_innerId)
, current(nullptr)
, currentWindow(0)
, winIconPreferred(false)
{
setApp(_app);
id = dock->allocEntryId();
name = getName();
icon = getIcon();
}
Entry::~Entry()
{
for (auto winInfo : windowInfos) {
if (winInfo)
delete winInfo;
}
windowInfos.clear();
if (app) {
delete app;
app = nullptr;
}
if (menu) {
delete menu;
menu = nullptr;
}
}
bool Entry::isValid()
{
bool valid = false;
if (app)
valid = app->isValidApp();
return valid;
}
QString Entry::getId()
{
return id;
}
QString Entry::getName()
{
QString ret;
if (app)
ret = app->getName();
if (ret.isEmpty() && !!current)
ret = current->getDisplayName();
return ret;
}
void Entry::updateName()
{
setPropName(getName());
}
QString Entry::getIcon()
{
QString ret;
if (hasWindow()) {
if (!current)
return ret;
// has window && current not nullptr
if (winIconPreferred) {
// try current window icon first
ret = current->getIcon();
if (ret.size() > 0)
return ret;
}
if (app) {
icon = app->getIcon();
if (icon.size() > 0)
return ret;
}
ret = current->getIcon();
} else if (app) {
// no window
ret = app->getIcon();
}
return ret;
}
QString Entry::getInnerId()
{
return innerId;
}
void Entry::setInnerId(QString _innerId)
{
innerId = _innerId;
}
QString Entry::getFileName()
{
QString fileName;
if (app)
fileName = app->getFileName();
return fileName;
}
AppInfo *Entry::getApp()
{
return app;
}
void Entry::setApp(AppInfo *appinfo)
{
if (app == appinfo)
return;
if (app)
delete app;
app = appinfo;
if (!appinfo) {
winIconPreferred = true;
setPropDesktopFile("");
} else {
winIconPreferred = false;
setPropDesktopFile(appinfo->getFileName());
QString id = app->getId();
auto perferredApps = dock->getWinIconPreferredApps();
if (perferredApps.contains(id)) {
winIconPreferred = true;
return;
}
if (appinfo->getIcon().size() == 0) {
winIconPreferred = true;
}
}
}
bool Entry::getIsDocked()
{
return isDocked;
}
void Entry::setIsDocked(bool value)
{
if (value != isDocked) {
isDocked = value;
Q_EMIT isDockedChanged(value);
}
}
// 导出Dbus服务
void Entry::startExport()
{
if (getId().isEmpty()) {
qWarning() << "startExport Entry " << getName() << " id is Empty";
return;
}
new DBusAdaptorEntry(this); // export dbus by Adaptor
QDBusConnection con = QDBusConnection::sessionBus();
if (!con.registerService(dbusService)) {
qWarning() << "register service Dock1 error:" << con.lastError().message();
return;
}
objctPath = entryDBusObjPathPrefix + getId();
if (!con.registerObject(objctPath, this))
{
qWarning() << "register object Dock1 error:" << con.lastError().message();
return;
}
}
// 停止导出Dbus服务
void Entry::stopExport()
{
if (getId().isEmpty()) {
qWarning() << "stopExport Entry " << getName() << " id is Empty";
return;
}
QDBusConnection con = QDBusConnection::sessionBus();
objctPath.clear();
con.unregisterObject(entryDBusObjPathPrefix + getId());
}
void Entry::setMenu(AppMenu *_menu)
{
_menu->setDirtyStatus(true);
if (menu)
delete menu;
menu = _menu;
Q_EMIT menuChanged(menu->getMenuJsonStr());
}
void Entry::updateMenu()
{
qInfo() <<"Entry: updateMenu";
AppMenu *appMenu = new AppMenu();
appMenu->appendItem(getMenuItemLaunch());
for (auto &item :getMenuItemDesktopActions())
appMenu->appendItem(item);
bool hasWin = hasWindow();
if (hasWin)
appMenu->appendItem(getMenuItemAllWindows());
// menu item dock or undock
qInfo() << "entry " << id << " docked? " << isDocked;
if (isDocked)
appMenu->appendItem(getMenuItemUndock());
else
appMenu->appendItem(getMenuItemDock());
if (hasWindow()) {
if (dock->getForceQuitAppStatus() != ForceQuitAppMode::Disabled) {
if (app && app->getIdentifyMethod() == "Andriod")
appMenu->appendItem(getMenuItemForceQuitAndroid());
else
appMenu->appendItem(getMenuItemForceQuit());
}
if (getAllowedCloseWindows().size() > 0)
appMenu->appendItem(getMenuItemCloseAll());
}
setMenu(appMenu);
}
void Entry::updateIcon()
{
setPropIcon(getIcon());
}
void Entry::forceUpdateIcon()
{
icon = getIcon();
Q_EMIT iconChanged(icon);
}
void Entry::updateIsActive()
{
bool isActive = false;
auto activeWin = dock->getActiveWindow();
if (activeWin)
isActive = windowInfos.find(activeWin->getXid()) != windowInfos.end();
setPropIsActive(isActive);
}
WindowInfoBase *Entry::getWindowInfoByPid(int pid)
{
WindowInfoBase *ret = nullptr;
for (const auto &windowInfo : windowInfos) {
if (windowInfo->getPid() == pid) {
ret = windowInfo;
break;
}
}
return ret;
}
WindowInfoBase *Entry::getWindowInfoByWinId(XWindow windowId)
{
WindowInfoBase *ret = nullptr;
if (windowInfos.find(windowId) != windowInfos.end()) {
ret = windowInfos[windowId];
}
return ret;
}
void Entry::setPropIsDocked(bool docked)
{
if (isDocked != docked) {
isDocked = docked;
Q_EMIT isDockedChanged(docked);
}
}
void Entry::setPropIcon(QString value)
{
if (value != icon) {
icon = value;
Q_EMIT iconChanged(value);
}
}
void Entry::setPropName(QString value)
{
if (value != name) {
name = value;
Q_EMIT nameChanged(value);
}
}
void Entry::setPropIsActive(bool active)
{
if (isActive != active) {
isActive = active;
Q_EMIT isActiveChanged(active);
}
}
void Entry::setCurrentWindowInfo(WindowInfoBase *windowInfo)
{
current = windowInfo;
setPropCurrentWindow(current ? current->getXid() : 0);
}
void Entry::setPropCurrentWindow(XWindow value)
{
if (value != currentWindow) {
currentWindow = value;
Q_EMIT currentWindowChanged(value);
}
}
WindowInfoBase *Entry::getCurrentWindowInfo()
{
return current;
}
QString Entry::getExec(bool oneLine)
{
QString ret;
if (!current)
return ret;
ProcessInfo *process = current->getProcess();
if (process) {
if (oneLine)
ret = process->getOneCommandLine().c_str();
else
ret = process->getShellScriptLines().c_str();
}
return ret;
}
bool Entry::hasWindow()
{
return windowInfos.size() > 0;
}
void Entry::updateWindowInfos()
{
QList<ExportWindowInfo> infos;
bool changed = false;
for (auto info : windowInfos) {
XWindow xid = info->getXid();
QString title = info->getTitle();
bool flash = info->isDemandingAttention();
infos.push_back({xid, title, flash});
if (!changed) {
for (auto info : exportWindowInfos) {
if (info.title != title || info.flash != flash)
changed = true;
}
}
}
if (changed) {
exportWindowInfos = infos;
}
}
// 分离窗口, 返回是否需要从任务栏remove
bool Entry::detachWindow(WindowInfoBase *info)
{
info->setEntry(nullptr);
XWindow winId = info->getXid();
deleteWindow(winId);
if (windowInfos.size() == 0) {
if (!isDocked) // 既无窗口也非驻留应用,无需在任务栏显示
return true;
setCurrentWindowInfo(nullptr);
} else {
for (auto window : windowInfos) {
if (window) { // 选择第一个窗口作为当前窗口
setCurrentWindowInfo(window);
break;
}
}
}
updateWindowInfos();
updateIcon();
updateIsActive();
updateMenu();
return false;
}
bool Entry::attachWindow(WindowInfoBase *info)
{
XWindow winId = info->getXid();
qInfo() << "attatchWindow: window id:" << winId;
info->setEntry(this);
if (windowInfos.find(winId) != windowInfos.end()) {
qInfo() << "attachWindow: window " << winId << " is already attached";
return false;
}
windowInfos[winId] = info;
updateWindowInfos();
updateIsActive();
if (!current) {
// from no window to has window
setCurrentWindowInfo(info);
}
updateIcon();
updateMenu();
return true;
}
void Entry::launchApp(uint32_t timestamp)
{
dock->launchApp(timestamp, QStringList() << app->getFileName());
}
bool Entry::containsWindow(XWindow xid)
{
return windowInfos.find(xid) != windowInfos.end();
}
void Entry::deleteWindow(XWindow xid)
{
WindowInfoBase *info = windowInfos[xid];
windowInfos.remove(xid);
for (int i = 0; i < exportWindowInfos.size(); i++) {
if (exportWindowInfos[i].xid == xid) {
exportWindowInfos.removeAt(i);
break;
}
}
if (info) {
delete info;
}
}
// 处理菜单项
void Entry::handleMenuItem(uint32_t timestamp, QString itemId)
{
menu->handleAction(timestamp, itemId);
}
// 处理拖拽事件
void Entry::handleDragDrop(uint32_t timestamp, QStringList files)
{
dock->launchApp(timestamp, files);
}
// 驻留
void Entry::requestDock()
{
if (dock->dockEntry(this)) {
dock->saveDockedApps();
}
}
// 取消驻留
void Entry::requestUndock()
{
dock->undockEntry(this);
}
void Entry::newInstance(uint32_t timestamp)
{
QStringList files;
dock->launchApp(timestamp, files);
}
// 检查应用窗口分离、合并状态
void Entry::check()
{
for (auto iter = windowInfos.begin(); iter != windowInfos.end(); iter++) {
dock->attachOrDetachWindow(iter.value());
}
}
// 强制退出
void Entry::forceQuit()
{
QMap<int, QVector<WindowInfoBase*>> pidWinInfoMap;
for (auto iter = windowInfos.begin(); iter != windowInfos.end(); iter++) {
int pid = iter.value()->getPid();
if (pid != 0) {
pidWinInfoMap[pid].push_back(iter.value());
} else {
iter.value()->killClient();
}
for (auto iter = pidWinInfoMap.begin(); iter != pidWinInfoMap.end(); iter++) {
if (!killProcess(iter.key())) { // kill pid
for (auto &info : iter.value()) { // kill window
info->killClient();
}
}
}
}
}
void Entry::presentWindows()
{
QList<uint> windows;
for (auto iter = windowInfos.begin(); iter != windowInfos.end(); iter++)
windows.push_back(iter.key());
dock->presentWindows(windows);
}
void Entry::active(uint32_t timestamp)
{
}
XWindow Entry::getCurrentWindow()
{
return currentWindow;
}
QString Entry::getDesktopFile()
{
return desktopFile;
}
bool Entry::getIsActive()
{
return isActive;
}
QString Entry::getMenu()
{
return menu->getMenuJsonStr();
}
QVector<XWindow> Entry::getAllowedClosedWindowIds()
{
QVector<XWindow> ret;
for (auto iter = windowInfos.begin(); iter != windowInfos.end(); iter++) {
WindowInfoBase *info = iter.value();
if (info && info->allowClose())
ret.push_back(iter.key());
}
return ret;
}
QList<ExportWindowInfo> Entry::getExportWindowInfos()
{
return exportWindowInfos;
}
QVector<WindowInfoBase *> Entry::getAllowedCloseWindows()
{
QVector<WindowInfoBase *> ret;
for (auto iter = windowInfos.begin(); iter != windowInfos.end(); iter++) {
WindowInfoBase *info = iter.value();
if (info && info->allowClose())
ret.push_back(info);
}
return ret;
}
QVector<AppMenuItem> Entry::getMenuItemDesktopActions()
{
QVector<AppMenuItem> ret;
if (!app)
return ret;
for (auto action : app->getActions()) {
AppMenuAction fn = [&](uint32_t timestamp) {
qInfo() << "do MenuItem: " << action.name.c_str();
dock->launchAppAction(timestamp, app->getFileName(), action.section.c_str());
};
AppMenuItem item;
item.text = action.name.c_str();
item.action = fn;
item.isActive = true;
ret.push_back(item);
}
return ret;
}
AppMenuItem Entry::getMenuItemLaunch()
{
QString itemName;
if (hasWindow())
itemName = getName();
else
itemName = "Open";
AppMenuAction fn = [&](uint32_t timestamp) {
qInfo() << "do MenuItem: Open";
this->launchApp(timestamp);
};
AppMenuItem item;
item.text = itemName;
item.action = fn;
item.isActive = true;
return item;
}
AppMenuItem Entry::getMenuItemCloseAll()
{
AppMenuAction fn = [&](uint32_t timestamp) {
qInfo() << "do MenuItem: Close All";
auto winInfos = getAllowedCloseWindows();
// 从大到小排序, 方便后面关闭窗口
for (int i = 0; i < winInfos.size() - 1; i++) {
for (int j = i + 1; j < winInfos.size(); j++) {
if (winInfos[i]->getCreatedTime() < winInfos[j]->getCreatedTime()) {
auto info = winInfos[i];
winInfos[i] = winInfos[j];
winInfos[j] = info;
}
}
}
for (auto info : winInfos) {
qInfo() << "close WindowId " << info->getXid();
info->close(timestamp);
}
};
AppMenuItem item;
item.text = "Close All";
item.action = fn;
item.isActive = true;
return item;
}
AppMenuItem Entry::getMenuItemForceQuit()
{
bool active = dock->getForceQuitAppStatus() != ForceQuitAppMode::Deactivated;
AppMenuAction fn = [&](uint32_t) {
qInfo() << "do MenuItem: Force Quit";
forceQuit();
};
AppMenuItem item;
item.text = "Force Quit";
item.action = fn;
item.isActive = active;
return item;
}
//dock栏上Android程序的Force Quit功能
AppMenuItem Entry::getMenuItemForceQuitAndroid()
{
bool active = dock->getForceQuitAppStatus() != ForceQuitAppMode::Deactivated;
auto allowedCloseWindows = getAllowedCloseWindows();
AppMenuAction fn = [](uint32_t){};
if (allowedCloseWindows.size() > 0) {
qInfo() << "do MenuItem: Force Quit";
AppMenuAction fn = [&](uint32_t timestamp) {
for (auto info : allowedCloseWindows) {
info->close(timestamp);
}
};
}
AppMenuItem item;
item.text = "Force Quit";
item.action = fn;
item.isActive = active;
return item;
}
AppMenuItem Entry::getMenuItemDock()
{
AppMenuItem item;
item.text = "Dock";
item.action = [&](uint32_t) {
qInfo() << "do MenuItem: Dock";
requestDock();
};
item.isActive = true;
return item;
}
AppMenuItem Entry::getMenuItemUndock()
{
AppMenuItem item;
item.text = "Undock";
item.action = [&](uint32_t) {
qInfo() << "do MenuItem: Undock";
requestUndock();
};
item.isActive = true;
return item;
}
AppMenuItem Entry::getMenuItemAllWindows()
{
AppMenuItem item;
item.text = "All Windows";
item.action = [&](uint32_t) {
qInfo() << "do MenuItem: All Windows";
presentWindows();
};
item.isActive = true;
item.hint = menuItemHintShowAllWindows;
return item;
}
bool Entry::killProcess(int pid)
{
bool ret = true;
Process p(pid);
if (p.isExist()) {
ret = !kill(pid, SIGTERM);
}
return ret;
}
bool Entry::setPropDesktopFile(QString value)
{
bool ret = false;
if (value != desktopFile) {
desktopFile = value;
ret = true;
Q_EMIT desktopFileChanged(value);
}
return ret;
}

149
src/modules/dock/entry.h Normal file
View File

@ -0,0 +1,149 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ENTRY_H
#define ENTRY_H
#include "appinfo.h"
#include "appmenu.h"
#include "windowinfobase.h"
#include <QMap>
#include <QVector>
#include <QObject>
struct ExportWindowInfo {
XWindow xid;
QString title;
bool flash;
};
// 单个应用类
class Dock;
class Entry: public QObject
{
Q_OBJECT
public:
Entry(Dock *_dock, AppInfo *_app, QString _innerId, QObject *parent = nullptr);
~Entry();
bool isValid();
QString getId();
QString getName();
void updateName();
QString getIcon();
QString getInnerId();
void setInnerId(QString _innerId);
QString getFileName();
AppInfo *getApp();
void setApp(AppInfo *appinfo);
bool getIsDocked();
void setIsDocked(bool value);
void startExport();
void stopExport();
void setMenu(AppMenu *_menu);
void updateMenu();
void updateIcon();
void forceUpdateIcon();
void updateIsActive();
WindowInfoBase *getWindowInfoByPid(int pid);
WindowInfoBase *getWindowInfoByWinId(XWindow windowId);
void setPropIsDocked(bool docked);
void setPropIcon(QString value);
void setPropName(QString value);
void setPropIsActive(bool active);
void setPropCurrentWindow(XWindow value);
void setCurrentWindowInfo(WindowInfoBase *windowInfo);
WindowInfoBase *getCurrentWindowInfo();
QString getExec(bool oneLine);
bool hasWindow();
void updateWindowInfos();
bool detachWindow(WindowInfoBase *info);
bool attachWindow(WindowInfoBase *info);
void launchApp(uint32_t timestamp);
bool containsWindow(XWindow xid);
void deleteWindow(XWindow xid);
void handleMenuItem(uint32_t timestamp, QString itemId);
void handleDragDrop(uint32_t timestamp, QStringList files);
void requestDock();
void requestUndock();
void newInstance(uint32_t timestamp);
void check();
void forceQuit();
void presentWindows();
void active(uint32_t timestamp);
XWindow getCurrentWindow();
QString getDesktopFile();
bool getIsActive();
QString getMenu();
QVector<XWindow> getAllowedClosedWindowIds();
QList<ExportWindowInfo> getExportWindowInfos();
public Q_SLOTS:
QVector<WindowInfoBase *> getAllowedCloseWindows();
Q_SIGNALS:
void isActiveChanged(bool value);
void isDockedChanged(bool value);
void menuChanged(QString value);
void iconChanged(QString value);
void nameChanged(QString value);
void desktopFileChanged(QString value);
void currentWindowChanged(uint32_t value);
private:
// 右键菜单项
QVector<AppMenuItem> getMenuItemDesktopActions();
AppMenuItem getMenuItemLaunch();
AppMenuItem getMenuItemCloseAll();
AppMenuItem getMenuItemForceQuit();
AppMenuItem getMenuItemForceQuitAndroid();
AppMenuItem getMenuItemDock();
AppMenuItem getMenuItemUndock();
AppMenuItem getMenuItemAllWindows();
bool killProcess(int pid);
bool setPropDesktopFile(QString value);
Dock *dock;
AppInfo *app;
AppMenu *menu;
bool isActive;
bool isDocked;
QString id;
QString name;
QString icon;
QString innerId;
QString desktopFile;
// Dbus属性直接放到interface上
QMap<XWindow, WindowInfoBase *> windowInfos; // 该应用所有窗口
QList<ExportWindowInfo> exportWindowInfos;
WindowInfoBase *current; // 当前窗口
XWindow currentWindow; //当前窗口Id
bool winIconPreferred;
QString objctPath;
};
#endif // ENTRY_H

View File

@ -0,0 +1,141 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "processinfo.h"
#include "dstring.h"
#include "dfile.h"
ProcessInfo::ProcessInfo(int pid)
: hasPid(true)
, process(Process(pid))
{
if (pid == 0)
return;
// exe
exe = process.getExe();
// cwd
cwd = process.getCwd();
// cmdline
cmdLine = process.getCmdLine();
// ppid
Status pstatus = process.getStatus();
if (pstatus.size() > 0) {
int ppid = process.getPpid();
}
// args
auto verifyExe = [](std::string exe, std::string cwd, std::string firstArg){
if (DFile::base(firstArg) == firstArg)
return true;
if (!DFile::isAbs(firstArg))
firstArg = cwd + firstArg;
return exe == firstArg;
};
if (!verifyExe(exe, cwd, cmdLine[0])) {
auto parts = DString::splitStr(cmdLine[0], ' ');
// try again
if (verifyExe(exe, cwd, parts[0])) {
for (int j = 1; j < parts.size(); j++) {
args.push_back(parts[j]);
}
for (int i = 1; i < cmdLine.size(); i++) {
args.push_back(cmdLine[i]);
}
}
} else {
for (int i = 1; i < cmdLine.size(); i++) {
args.push_back(cmdLine[i]);
}
}
}
ProcessInfo::ProcessInfo(std::vector<std::string> &cmd)
: hasPid(false)
, process(Process())
{
if (cmd.size() == 0)
return;
cmdLine = cmd;
exe = cmd[0];
for (ulong i=0; i < cmd.size(); i++) {
if (i > 0) {
args.push_back(cmd[i]);
}
}
}
std::string ProcessInfo::getEnv(std::string key)
{
return process.getEnv(key);
}
std::vector<std::string> ProcessInfo::getCmdLine()
{
return cmdLine;
}
std::vector<std::string> ProcessInfo::getArgs()
{
return args;
}
int ProcessInfo::getPid()
{
return process.getPid();
}
int ProcessInfo::getPpid()
{
return process.getPpid();
}
std::string ProcessInfo::getExe()
{
return exe;
}
std::string ProcessInfo::getOneCommandLine()
{
std::string cmdline = getJoinedExeArgs();
return "sh -c 'cd " + cwd + "; exec " + cmdline + ";'";
}
std::string ProcessInfo::getShellScriptLines()
{
std::string cmdline = getJoinedExeArgs();
return "#!/bin/sh\n cd " + cwd + "\n exec " + cmdline + "\n";
}
std::string ProcessInfo::getJoinedExeArgs()
{
std::string ret = "\"" + exe + "\"";
for (auto arg : args) {
ret += " \"" + arg + "\"";
}
ret += " $@";
return ret;
}

View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PROCESSINFO_H
#define PROCESSINFO_H
#include "process.h"
#include <string>
#include <vector>
// 进程信息
class ProcessInfo
{
public:
ProcessInfo(int pid);
ProcessInfo(std::vector<std::string> &cmd);
std::string getEnv(std::string key);
std::vector<std::string> getCmdLine();
std::vector<std::string> getArgs();
int getPid();
int getPpid();
std::string getExe();
std::string getOneCommandLine();
std::string getShellScriptLines();
private:
std::string getJoinedExeArgs();
std::vector<std::string> cmdLine;
std::vector<std::string> args;
std::string exe;
std::string cwd;
bool hasPid;
Process process;
};
#endif // PROCESSINFO_H

View File

@ -0,0 +1,127 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "waylandmanager.h"
#include "dock.h"
#include "xcbutils.h"
#define XCB XCBUtils::instance()
WaylandManager::WaylandManager(Dock *_dock, QObject *parent)
: QObject(parent)
, dock(_dock)
, mutex(QMutex(QMutex::NonRecursive))
{
}
// 注册窗口
void WaylandManager::registerWindow(const QString &objPath)
{
qInfo() << "registerWindow: " << objPath;
if (findWindowByObjPath(objPath))
return;
PlasmaWindow *plasmaWindow = dock->createPlasmaWindow(objPath);
if (!plasmaWindow) {
qWarning() << "registerWindowWayland: createPlasmaWindow failed";
return;
}
QString appId = plasmaWindow->AppId();
QStringList list {"dde-dock", "dde-launcher", "dde-clipboard", "dde-osd", "dde-polkit-agent", "dde-simple-egl", "dmcs"};
if (list.indexOf(appId))
return;
XWindow winId = XCB->allocId(); // XCB中未发现释放XID接口
XWindow realId = plasmaWindow->WindowId();
if (!realId)
winId = realId;
WindowInfoK *winInfo = new WindowInfoK(plasmaWindow, winId);
dock->listenKWindowSignals(winInfo);
insertWindow(objPath, winInfo);
dock->attachOrDetachWindow(winInfo);
if (realId) {
windowInfoMap[realId] = winInfo;
}
}
// 取消注册窗口
void WaylandManager::unRegisterWindow(const QString &objPath)
{
qInfo() << "unRegisterWindow: " << objPath;
WindowInfoK *winInfo = findWindowByObjPath(objPath);
if (!winInfo)
return;
dock->removePlasmaWindowHandler(winInfo->getPlasmaWindow());
dock->detachWindow(winInfo);
deleteWindow(objPath);
}
WindowInfoK *WaylandManager::handleActiveWindowChangedK(uint activeWin)
{
WindowInfoK *winInfo = nullptr;
QMutexLocker locker(&mutex);
for (auto iter = kWinInfos.begin(); iter != kWinInfos.end(); iter++) {
if (iter.value()->getInnerId() == activeWin) {
winInfo = iter.value();
break;
}
}
return winInfo;
}
WindowInfoK *WaylandManager::findWindowByXid(XWindow xid)
{
WindowInfoK *winInfo = nullptr;
for (auto iter = kWinInfos.begin(); iter != kWinInfos.end(); iter++) {
if (iter.value()->getXid() == xid) {
winInfo = iter.value();
break;
}
}
return winInfo;
}
WindowInfoK *WaylandManager::findWindowByObjPath(QString objPath)
{
if (kWinInfos.find(objPath) == kWinInfos.end())
return nullptr;
return kWinInfos[objPath];
}
void WaylandManager::insertWindow(QString objPath, WindowInfoK *windowInfo)
{
QMutexLocker locker(&mutex);
kWinInfos[objPath] = windowInfo;
}
void WaylandManager::deleteWindow(QString objPath)
{
kWinInfos.remove(objPath);
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WAYLANDMANAGER_H
#define WAYLANDMANAGER_H
#include "windowinfok.h"
#include <QObject>
#include <QMap>
#include <QMutex>
class Dock;
// 管理wayland窗口
class WaylandManager : public QObject
{
Q_OBJECT
public:
explicit WaylandManager(Dock *_dock, QObject *parent = nullptr);
void registerWindow(const QString &objPath);
void unRegisterWindow(const QString &objPath);
WindowInfoK *handleActiveWindowChangedK(uint activeWin);
WindowInfoK *findWindowByXid(XWindow xid);
WindowInfoK *findWindowByObjPath(QString objPath);
void insertWindow(QString objPath, WindowInfoK *windowInfo);
void deleteWindow(QString objPath);
private:
Dock *dock;
QMap<QString, WindowInfoK *> kWinInfos; // dbusObjectPath -> kwayland window Info
QMap<XWindow, WindowInfoK *> windowInfoMap;
QMutex mutex;
};
#endif // WAYLANDMANAGER_H

View File

@ -0,0 +1,545 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "windowidentify.h"
#include "common.h"
#include "appinfo.h"
#include "dock.h"
#include "processinfo.h"
#include "dstring.h"
#include "basedir.h"
#include "dfile.h"
#include "xcbutils.h"
#include <QDebug>
#include <QThread>
#define XCB XCBUtils::instance()
WindowPatterns WindowIdentify::patterns;
static QMap<QString, QString> crxAppIdMap = {
{"crx_onfalgmmmaighfmjgegnamdjmhpjpgpi", "apps.com.aiqiyi"},
{"crx_gfhkopakpiiaeocgofdpcpjpdiglpkjl", "apps.cn.kugou.hd"},
{"crx_gaoopbnflngfkoobibfgbhobdeiipcgh", "apps.cn.kuwo.kwmusic"},
{"crx_jajaphleehpmpblokgighfjneejapnok", "apps.com.evernote"},
{"crx_ebhffdbfjilfhahiinoijchmlceailfn", "apps.com.letv"},
{"crx_almpoflgiciaanepplakjdkiaijmklld", "apps.com.tongyong.xxbox"},
{"crx_heaphplipeblmpflpdcedfllmbehonfo", "apps.com.peashooter"},
{"crx_dbngidmdhcooejaggjiochbafiaefndn", "apps.com.rovio.angrybirdsseasons"},
{"crx_chfeacahlaknlmjhiagghobhkollfhip", "apps.com.sina.weibo"},
{"crx_cpbmecbkmjjfemjiekledmejoakfkpec", "apps.com.openapp"},
{"crx_lalomppgkdieklbppclocckjpibnlpjc", "apps.com.baidutieba"},
{"crx_gejbkhjjmicgnhcdpgpggboldigfhgli", "apps.com.zhuishushenqi"},
{"crx_gglenfcpioacendmikabbkecnfpanegk", "apps.com.duokan"},
{"crx_nkmmgdfgabhefacpfdabadjfnpffhpio", "apps.com.zhihu.daily"},
{"crx_ajkogonhhcighbinfgcgnjiadodpdicb", "apps.com.netease.newsreader"},
{"crx_hgggjnaaklhemplabjhgpodlcnndhppo", "apps.com.baidu.music.pad"},
{"crx_ebmgfebnlgilhandilnbmgadajhkkmob", "apps.cn.ibuka"},
{"crx_nolebplcbgieabkblgiaacdpgehlopag", "apps.com.tianqitong"},
{"crx_maghncnmccfbmkekccpmkjjfcmdnnlip", "apps.com.youjoy.strugglelandlord"},
{"crx_heliimhfjgfabpgfecgdhackhelmocic", "apps.cn.emoney"},
{"crx_jkgmneeafmgjillhgmjbaipnakfiidpm", "apps.com.instagram"},
{"crx_cdbkhmfmikobpndfhiphdbkjklbmnakg", "apps.com.easymindmap"},
{"crx_djflcciklfljleibeinjmjdnmenkciab", "apps.com.lgj.thunderbattle"},
{"crx_ffdgbolnndgeflkapnmoefhjhkeilfff", "apps.com.qianlong"},
{"crx_fmpniepgiofckbfgahajldgoelogdoap", "apps.com.windhd"},
{"crx_dokjmallmkihbgefmladclcdcinjlnpj", "apps.com.youdao.hanyu"},
{"crx_dicimeimfmbfcklbjdpnlmjgegcfilhm", "apps.com.ibookstar"},
{"crx_cokkcjnpjfffianjbpjbcgjefningkjm", "apps.com.yidianzixun"},
{"crx_ehflkacdpmeehailmcknlnkmjalehdah", "apps.com.xplane"},
{"crx_iedokjbbjejfinokgifgecmboncmkbhb", "apps.com.wedevote"},
{"crx_eaefcagiihjpndconigdpdmcbpcamaok", "apps.com.tongwei.blockbreaker"},
{"crx_mkjjfibpccammnliaalefmlekiiikikj", "apps.com.dayima"},
{"crx_gflkpppiigdigkemnjlonilmglokliol", "apps.com.cookpad"},
{"crx_jfhpkchgedddadekfeganigbenbfaohe", "apps.com.issuu"},
{"crx_ggkmfnbkldhmkehabgcbnmlccfbnoldo", "apps.bible.cbol"},
{"crx_phlhkholfcljapmcidanddmhpcphlfng", "apps.com.kanjian.radio"},
{"crx_bjgfcighhaahojkibojkdmpdihhcehfm", "apps.de.danoeh.antennapod"},
{"crx_kldipknjommdfkifomkmcpbcnpmcnbfi", "apps.com.asoftmurmur"},
{"crx_jfhlegimcipljdcionjbipealofoncmd", "apps.com.tencentnews"},
{"crx_aikgmfkpmmclmpooohngmcdimgcocoaj", "apps.com.tonghuashun"},
{"crx_ifimglalpdeoaffjmmihoofapmpflkad", "apps.com.letv.lecloud.disk"},
{"crx_pllcekmbablpiogkinogefpdjkmgicbp", "apps.com.hwadzanebook"},
{"crx_ohcknkkbjmgdfcejpbmhjbohnepcagkc", "apps.com.douban.radio"},
};
WindowIdentify::WindowIdentify(Dock *_dock, QObject *parent)
: QObject(parent)
, dock(_dock)
{
identifyWindowFuns["Android"] = identifyWindowAndroid;
identifyWindowFuns["PidEnv"] = identifyWindowByPidEnv;
identifyWindowFuns["CmdlineTurboBooster"] = identifyWindowByCmdlineTurboBooster;
identifyWindowFuns["Cmdline-XWalk"] = identifyWindowByCmdlineXWalk;
identifyWindowFuns["FlatpakAppID"] = identifyWindowByFlatpakAppID;
identifyWindowFuns["CrxId"] = identifyWindowByCrxId;
identifyWindowFuns["Rule"] = identifyWindowByRule;
identifyWindowFuns["Bamf"] = identifyWindowByBamf;
identifyWindowFuns["Pid"] = identifyWindowByPid;
identifyWindowFuns["Scratch"] = identifyWindowByScratch;
identifyWindowFuns["GtkAppId"] = identifyWindowByGtkAppId;
identifyWindowFuns["WmClass"] = identifyWindowByWmClass;
}
AppInfo *WindowIdentify::identifyWindow(WindowInfoBase *winInfo, QString &innerId)
{
AppInfo *ret = nullptr;
//qInfo() << "identifyWindow: window id " << winInfo->getXid() << " innerId " << winInfo->getInnerId();
if (winInfo->getWindowType() == "X11")
ret = identifyWindowX11(static_cast<WindowInfoX *>(winInfo), innerId);
else if (winInfo->getWindowType() == "Wayland")
ret = identifyWindowWayland(static_cast<WindowInfoK *>(winInfo), innerId);
return ret;
}
AppInfo *WindowIdentify::identifyWindowX11(WindowInfoX *winInfo, QString &innerId)
{
AppInfo *appInfo = nullptr;
if (winInfo->getInnerId() == "") {
qInfo() << "identify WindowX11: innerId is empty";
return appInfo;
}
for (auto iter = identifyWindowFuns.begin(); iter != identifyWindowFuns.end(); iter++) {
QString name = iter.key();
IdentifyFunc func = iter.value();
qInfo() << "identifyWindowX11: try " << name;
appInfo = func(dock, winInfo, innerId);
if (appInfo) { // TODO: if name == "Pid", appInfo may by nullptr
// 识别成功
qInfo() << "identify Window by " << name << " innerId " << appInfo->getInnerId() << " success!";
AppInfo *fixedAppInfo = fixAutostartAppInfo(appInfo->getFileName());
if (fixedAppInfo) {
delete appInfo;
appInfo = fixedAppInfo;
appInfo->setIdentifyMethod(name + "+FixAutostart");
innerId = appInfo->getInnerId();
} else {
appInfo->setIdentifyMethod(name);
}
return appInfo;
}
}
qInfo() << "identifyWindowX11: failed";
return appInfo;
}
AppInfo *WindowIdentify::identifyWindowWayland(WindowInfoK *winInfo, QString &innerId)
{
// TODO: 对桌面调起的文管应用做规避处理需要在此处添加因为初始化时appId和title为空
if (winInfo->getAppId() == "dde-desktop" && dock->shouldShowOnDock(winInfo)) {
winInfo->setAppId("dde-file-manager");
}
QString appId = winInfo->getAppId();
if (appId.isEmpty()) {
QString title = winInfo->getTitle();
// TODO: 对于appId为空的情况使用title过滤此项修改针对浏览器下载窗口
}
// 先使用appId获取appInfo,如果不能成功获取再使用GIO_LAUNCHED_DESKTOP_FILE环境变量获取
AppInfo *appInfo = new AppInfo(appId);
if (!appInfo->isValidApp() && winInfo->getProcess()) {
ProcessInfo *process = winInfo->getProcess();
std::string desktopFilePath = process->getEnv("GIO_LAUNCHED_DESKTOP_FILE");
if (DString::endWith(desktopFilePath, ".desktop")) {
appInfo = new AppInfo(desktopFilePath.c_str());
}
}
// autoStart
if (appInfo->isValidApp()) {
AppInfo *fixedAppInfo = fixAutostartAppInfo(appInfo->getFileName());
if (fixedAppInfo) {
delete appInfo;
appInfo = fixedAppInfo;
appInfo->setIdentifyMethod("FixAutostart");
}
} else {
// bamf
XWindow winId = winInfo->getXid();
QString desktop = dock->getDesktopFromWindowByBamf(winId);
if (!desktop.isEmpty()) {
appInfo = new AppInfo(desktop);
}
WMClass wmClass = XCB->getWMClass(winId);
if (wmClass.instanceName.size() > 0) {
QString instance(wmClass.instanceName.c_str());
appInfo = new AppInfo("org.deepin.flatdeb." + instance);
if (appInfo)
return appInfo;
appInfo = new AppInfo(instance);
if (appInfo)
return appInfo;
if (wmClass.className.size() > 0) {
appInfo = new AppInfo(wmClass.className.c_str());
if (appInfo)
return appInfo;
}
}
}
if (appInfo)
innerId = appInfo->getInnerId();
return appInfo;
}
AppInfo *WindowIdentify::identifyWindowAndroid(Dock *_dock, WindowInfoX *winInfo, QString &innerId)
{
AppInfo *ret = nullptr;
int32_t androidId = getAndroidUengineId(winInfo->getXid());
QString androidName = getAndroidUengineName(winInfo->getXid());
if (androidId != -1 && androidName != "") {
QString desktopPath = "/usr/share/applications/uengine." + androidName + ".desktop";
DesktopInfo desktopInfo(desktopPath.toStdString());
if (!desktopInfo.isValidDesktop()) {
qInfo() << "identifyWindowAndroid: not exist DesktopFile " << desktopPath;
return ret;
}
ret = new AppInfo(desktopInfo);
ret->setIdentifyMethod("Android");
innerId = ret->getInnerId();
}
return ret;
}
AppInfo *WindowIdentify::identifyWindowByPidEnv(Dock *_dock, WindowInfoX *winInfo, QString &innerId)
{
AppInfo *ret = nullptr;
int pid = winInfo->getPid();
auto process = winInfo->getProcess();
qInfo() << "identifyWindowByPidEnv: pid=" << pid << " WindowId=" << winInfo->getXid();
if (pid != 0 && process) {
QString launchedDesktopFile = process->getEnv("GIO_LAUNCHED_DESKTOP_FILE").c_str();
QString launchedDesktopFilePidStr = process->getEnv("GIO_LAUNCHED_DESKTOP_FILE_PID").c_str();
int launchedDesktopFilePid = launchedDesktopFilePidStr.toInt();
qInfo() << "launchedDesktopFilePid=" << launchedDesktopFilePid << " launchedDesktopFile=" << launchedDesktopFile;
// 以下 2 种情况下,才能信任环境变量 GIO_LAUNCHED_DESKTOP_FILE。
// 1. 当窗口 pid 和 launchedDesktopFilePid 相同时;
// 2. 当窗口的进程的父进程 id即 ppid和 launchedDesktopFilePid 相同,
// 并且该父进程是 sh 或 bash 时。
bool needTry = false;
if (pid == launchedDesktopFilePid) {
needTry = true;
} else if (process->getPpid() && process->getPpid() == launchedDesktopFilePid) {
Process parentProcess(launchedDesktopFilePid);
auto parentCmdLine = parentProcess.getCmdLine();
if (parentCmdLine.size() > 0) {
qInfo() << "ppid equal " << "parentCmdLine[0]:" << parentCmdLine[0].c_str();
QString cmd0 = parentCmdLine[0].c_str();
int pos = cmd0.lastIndexOf('/');
if (pos > 0)
cmd0 = cmd0.remove(0, pos + 1);
if (cmd0 == "sh" || cmd0 == "bash")
needTry = true;
}
}
if (needTry) {
ret = new AppInfo(launchedDesktopFile);
innerId = ret->getInnerId();
}
}
return ret;
}
AppInfo *WindowIdentify::identifyWindowByCmdlineTurboBooster(Dock *_dock, WindowInfoX *winInfo, QString &innerId)
{
AppInfo *ret = nullptr;
int pid = winInfo->getPid();
ProcessInfo *process = winInfo->getProcess();
if (pid != 0 && process) {
auto cmdline = process->getCmdLine();
if (cmdline.size() > 0) {
QString desktopFile;
if (DString::endWith(cmdline[0], ".desktop")) {
desktopFile = cmdline[0].c_str();
} else if (QString(cmdline[0].c_str()).contains("/applications/")) {
QFileInfo fileInfo(cmdline[0].c_str());
QString path = fileInfo.path();
QString base = fileInfo.baseName();
QDir dir(path);
QStringList files = dir.entryList(QDir::Files);
for (auto f : files) {
if (f.contains(path + "/" + base + ".desktop")) {
desktopFile = f;
break;
}
}
qInfo() << "identifyWindowByCmdlineTurboBooster: desktopFile is " << desktopFile;
if (!desktopFile.isEmpty()) {
ret = new AppInfo(desktopFile);
innerId = ret->getInnerId();
}
}
}
}
return ret;
}
AppInfo *WindowIdentify::identifyWindowByCmdlineXWalk(Dock *_dock, WindowInfoX *winInfo, QString &innerId)
{
qInfo() << "identifyWindowByCmdlineXWalk: windowId=" << winInfo->getXid();
AppInfo *ret = nullptr;
do {
auto process = winInfo->getProcess();
if (!process || !winInfo->getPid())
break;
QString exe = process->getExe().c_str();
QFileInfo file(exe);
QString exeBase = file.baseName();
auto args = process->getArgs();
if (exe != "xwalk" || args.size() == 0)
break;
QString lastArg = args[args.size() - 1].c_str();
file.setFile(lastArg);
if (file.baseName() == "manifest.json") {
auto strs = lastArg.split("/");
if (strs.size() > 3 && strs[strs.size() - 2].size() > 0) { // appId为 strs倒数第二个字符串
ret = new AppInfo(strs[strs.size() - 2]);
innerId = ret->getInnerId();
break;
}
}
qInfo() << "identifyWindowByCmdlineXWalk: failed";
} while (0);
return ret;
}
AppInfo *WindowIdentify::identifyWindowByFlatpakAppID(Dock *_dock, WindowInfoX *winInfo, QString &innerId)
{
AppInfo *ret = nullptr;
QString flatpak = winInfo->getFlatpakAppId();
qInfo() << "identifyWindowByFlatpakAppID: flatpak:" << flatpak;
if (flatpak.startsWith("app/")) {
auto parts = flatpak.split("/");
if (parts.size() > 0) {
QString appId = parts[1];
ret = new AppInfo(appId);
innerId = ret->getInnerId();
}
}
return ret;
}
AppInfo *WindowIdentify::identifyWindowByCrxId(Dock *_dock, WindowInfoX *winInfo, QString &innerId)
{
AppInfo *ret = nullptr;
WMClass wmClass = XCB->getWMClass(winInfo->getXid());
QString className, instanceName;
className.append(wmClass.className.c_str());
instanceName.append(wmClass.instanceName.c_str());
if (className.toLower() == "chromium-browser" && instanceName.toLower().startsWith("crx_")) {
if (crxAppIdMap.find(instanceName.toLower()) != crxAppIdMap.end()) {
QString appId = crxAppIdMap[instanceName.toLower()];
qInfo() << "identifyWindowByCrxId: appId " << appId;
ret = new AppInfo(appId);
innerId = ret->getInnerId();
}
}
return ret;
}
AppInfo *WindowIdentify::identifyWindowByRule(Dock *_dock, WindowInfoX *winInfo, QString &innerId)
{
qInfo() << "identifyWindowByRule: windowId=" << winInfo->getXid();
AppInfo *ret = nullptr;
QString matchStr = patterns.match(winInfo);
if (matchStr.isEmpty())
return ret;
if (matchStr.size() > 4 && matchStr.startsWith("id=")) {
matchStr.remove(0, 3);
ret = new AppInfo(matchStr);
} else if (matchStr == "env") {
auto process = winInfo->getProcess();
if (process) {
QString launchedDesktopFile = process->getEnv("GIO_LAUNCHED_DESKTOP_FILE").c_str();
if (!launchedDesktopFile.isEmpty())
ret = new AppInfo(launchedDesktopFile);
}
} else {
qInfo() << "patterns match bad result";
}
if (ret)
innerId = ret->getInnerId();
return ret;
}
AppInfo *WindowIdentify::identifyWindowByBamf(Dock *_dock, WindowInfoX *winInfo, QString &innerId)
{
AppInfo *ret = nullptr;
XWindow xid = winInfo->getXid();
qInfo() << "identifyWindowByBamf: windowId=" << xid;
QString desktopFile;
// 重试 bamf 识别yozo office 的窗口经常要第二次时才能识别到。
for (int i=0; i<3; i++) {
desktopFile = _dock->getDesktopFromWindowByBamf(xid);
if (!desktopFile.isEmpty())
break;
QThread::msleep(100);
}
if (!desktopFile.isEmpty()) {
ret = new AppInfo(desktopFile);
innerId = ret->getInnerId();
}
return ret;
}
AppInfo *WindowIdentify::identifyWindowByPid(Dock *_dock, WindowInfoX *winInfo, QString &innerId)
{
AppInfo *ret = nullptr;
if (winInfo->getPid() > 10) {
auto entry = _dock->getEntryByWindowId(winInfo->getPid());
if (entry) {
ret = entry->getApp();
innerId = ret->getInnerId();
}
}
return ret;
}
AppInfo *WindowIdentify::identifyWindowByScratch(Dock *_dock, WindowInfoX *winInfo, QString &innerId)
{
AppInfo *ret = nullptr;
QString desktopFile = scratchDir + winInfo->getInnerId() + ".desktop";
qInfo() << "identifyWindowByScratch: xid " << winInfo->getXid() << " desktopFile" << desktopFile;
QFile file(desktopFile);
if (file.exists()) {
ret = new AppInfo(desktopFile);
innerId = ret->getInnerId();
}
return ret;
}
AppInfo *WindowIdentify::identifyWindowByGtkAppId(Dock *_dock, WindowInfoX *winInfo, QString &innerId)
{
AppInfo *ret = nullptr;
QString gtkAppId = winInfo->getGtkAppId();
if (!gtkAppId.isEmpty()) {
ret = new AppInfo(gtkAppId);
innerId = ret->getInnerId();
}
qInfo() << "identifyWindowByGtkAppId: gtkAppId:" << gtkAppId;
return ret;
}
AppInfo *WindowIdentify::identifyWindowByWmClass(Dock *_dock, WindowInfoX *winInfo, QString &innerId)
{
AppInfo *ret = nullptr;
WMClass wmClass = winInfo->getWMClass();
do {
if (wmClass.instanceName.size() > 0) {
// example:
// WM_CLASS(STRING) = "Brackets", "Brackets"
// wm class instance is Brackets
// try app id org.deepin.flatdeb.brackets
ret = new AppInfo("org.deepin.flatdeb." + QString(wmClass.instanceName.c_str()).toLower());
if (ret)
break;
ret = new AppInfo(wmClass.instanceName.c_str());
if (ret)
break;
}
if (wmClass.className.size() > 0) {
ret = new AppInfo(wmClass.className.c_str());
if (ret)
break;
}
} while (0);
if (ret)
innerId = ret->getInnerId();
return ret;
}
AppInfo *WindowIdentify::fixAutostartAppInfo(QString fileName)
{
QFileInfo file(fileName);
QString filePath = file.absolutePath();
bool isAutoStart = false;
for (auto &dir : BaseDir::autoStartDirs()) {
if (QString(dir.c_str()).contains(filePath)) {
isAutoStart = true;
break;
}
}
return isAutoStart ? new AppInfo(file.baseName()) : nullptr;
}
int32_t WindowIdentify::getAndroidUengineId(XWindow winId)
{
// TODO 获取AndroidUengineId
return 0;
}
QString WindowIdentify::getAndroidUengineName(XWindow winId)
{
// TODO 获取AndroidUengineName
return "";
}

View File

@ -0,0 +1,75 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WINDOWIDENTIFY_H
#define WINDOWIDENTIFY_H
#include "windowpatterns.h"
#include "windowinfok.h"
#include "windowinfox.h"
#include <QObject>
#include <QVector>
#include <QMap>
class AppInfo;
class Dock;
typedef AppInfo *(*IdentifyFunc)(Dock *, WindowInfoX*, QString &innerId);
// 应用窗口识别类
class WindowIdentify : public QObject
{
Q_OBJECT
public:
explicit WindowIdentify(Dock *_dock, QObject *parent = nullptr);
AppInfo *identifyWindow(WindowInfoBase *winInfo, QString &innerId);
AppInfo *identifyWindowX11(WindowInfoX *winInfo, QString &innerId);
AppInfo *identifyWindowWayland(WindowInfoK *winInfo, QString &innerId);
static AppInfo *identifyWindowAndroid(Dock *_dock, WindowInfoX *winInfo, QString &innerId);
static AppInfo *identifyWindowByPidEnv(Dock *_dock, WindowInfoX *winInfo, QString &innerId);
static AppInfo *identifyWindowByCmdlineTurboBooster(Dock *_dock, WindowInfoX *winInfo, QString &innerId);
static AppInfo *identifyWindowByCmdlineXWalk(Dock *_dock, WindowInfoX *winInfo, QString &innerId);
static AppInfo *identifyWindowByFlatpakAppID(Dock *_dock, WindowInfoX *winInfo, QString &innerId);
static AppInfo *identifyWindowByCrxId(Dock *_dock, WindowInfoX *winInfo, QString &innerId);
static AppInfo *identifyWindowByRule(Dock *_dock, WindowInfoX *winInfo, QString &innerId);
static AppInfo *identifyWindowByBamf(Dock *_dock, WindowInfoX *winInfo, QString &innerId);
static AppInfo *identifyWindowByPid(Dock *_dock, WindowInfoX *winInfo, QString &innerId);
static AppInfo *identifyWindowByScratch(Dock *_dock, WindowInfoX *winInfo, QString &innerId);
static AppInfo *identifyWindowByGtkAppId(Dock *_dock, WindowInfoX *winInfo, QString &innerId);
static AppInfo *identifyWindowByWmClass(Dock *_dock, WindowInfoX *winInfo, QString &innerId);
public Q_SLOTS:
private:
AppInfo *fixAutostartAppInfo(QString fileName);
static int32_t getAndroidUengineId(XWindow winId);
static QString getAndroidUengineName(XWindow winId);
static WindowPatterns patterns; // 根据rule识别
Dock *dock;
QMap<QString, IdentifyFunc> identifyWindowFuns;
};
#endif // IDENTIFYWINDOW_H

View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WINDOWINFOBASE_H
#define WINDOWINFOBASE_H
#include "xcbutils.h"
#include <QString>
#include <QVector>
class Entry;
class AppInfo;
class ProcessInfo;
class WindowInfoBase
{
public:
WindowInfoBase() : entry(nullptr), app(nullptr), processInfo(nullptr) {}
virtual ~WindowInfoBase() {}
virtual bool shouldSkip() = 0;
virtual QString getIcon() = 0;
virtual QString getTitle() = 0;
virtual bool isDemandingAttention() = 0;
virtual void close(uint32_t timestamp) = 0;
virtual void activate() = 0;
virtual void minimize() = 0;
virtual bool isMinimized() = 0;
virtual int64_t getCreatedTime() = 0;
virtual QString getWindowType() = 0;
virtual QString getDisplayName() = 0;
virtual bool allowClose() = 0;
virtual void update() = 0;
virtual void killClient() = 0;
XWindow getXid() {return xid;}
void setEntry(Entry *value) {entry = value;}
Entry *getEntry() {return entry;}
QString getEntryInnerId() {return entryInnerId;}
QString getInnerId() {return innerId;}
void setEntryInnerId(QString value) {entryInnerId = value;}
AppInfo *getAppInfo() {return app;}
void setAppInfo(AppInfo *value) {app = value;}
int getPid() {return pid;}
ProcessInfo *getProcess() {return processInfo;}
bool containAtom(QVector<XCBAtom> supports, XCBAtom ty) {return supports.indexOf(ty) != -1;}
protected:
XWindow xid;
QString title;
QString icon;
int pid;
QString entryInnerId;
QString innerId;
Entry *entry;
AppInfo *app;
ProcessInfo *processInfo;
int64_t createdTime;
};
#endif // WINDOWINFOBASE_H

View File

@ -0,0 +1,210 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "windowinfok.h"
#include "entry.h"
#include "processinfo.h"
#include "appinfo.h"
#include <chrono>
WindowInfoK::WindowInfoK(PlasmaWindow *window, XWindow _xid)
: WindowInfoBase ()
, updateCalled(false)
, internalId(0)
, demaningAttention(false)
, closeable(true)
, minimized(true)
, plasmaWindow(window)
{
xid = _xid;
createdTime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); // 获取当前时间,精确到纳秒
}
WindowInfoK::~WindowInfoK()
{
}
bool WindowInfoK::shouldSkip()
{
if (!updateCalled) {
update();
updateCalled = true;
}
bool skip = plasmaWindow->SkipTaskbar();
// 添加窗口能否最小化判断, 如果窗口不能最小化则隐藏任务栏图标
bool canMinimize = false;
canMinimize = plasmaWindow->IsMinimizeable();
if (!canMinimize)
skip = true;
if (skip) {
// 白名单, 过滤类似“欢迎应用”, 没有最小化窗口但是需要在任务栏显示图标
QStringList list { "dde-introduction"};
if (list.indexOf(appId) != -1)
skip = false;
}
return skip;
}
QString WindowInfoK::getIcon()
{
return icon;
}
QString WindowInfoK::getTitle()
{
return title;
}
bool WindowInfoK::isDemandingAttention()
{
return demaningAttention;
}
bool WindowInfoK::allowClose()
{
return closeable;
}
void WindowInfoK::close(uint32_t timestamp)
{
plasmaWindow->RequestClose();
}
QString WindowInfoK::getAppId()
{
return appId;
}
void WindowInfoK::setAppId(QString _appId)
{
appId = _appId;
}
void WindowInfoK::activate()
{
plasmaWindow->RequestActivate();
}
void WindowInfoK::minimize()
{
plasmaWindow->RequestToggleMinimized();
}
bool WindowInfoK::isMinimized()
{
return minimized;
}
bool WindowInfoK::changeXid(XWindow _xid)
{
xid = _xid;
return true;
}
PlasmaWindow *WindowInfoK::getPlasmaWindow()
{
return plasmaWindow;
}
bool WindowInfoK::updateGeometry()
{
Rect rect = plasmaWindow->Geometry();
if (geometry == rect)
return false;
geometry = rect;
return true;
}
void WindowInfoK::updateTitle()
{
title = plasmaWindow->Title();
}
void WindowInfoK::updateDemandingAttention()
{
demaningAttention = plasmaWindow->IsDemandingAttention();
}
void WindowInfoK::updateIcon()
{
icon = plasmaWindow->Icon();
}
void WindowInfoK::updateAppId()
{
appId = plasmaWindow->AppId();
}
void WindowInfoK::updateInternalId()
{
internalId = plasmaWindow->InternalId();
}
void WindowInfoK::updateCloseable()
{
closeable = plasmaWindow->IsCloseable();
}
void WindowInfoK::updateProcessInfo()
{
pid = plasmaWindow->Pid();
processInfo = new ProcessInfo(pid);
}
int64_t WindowInfoK::getCreatedTime()
{
return createdTime;
}
// 主要是为兼容X11
QString WindowInfoK::getDisplayName()
{
return "";
}
QString WindowInfoK::getWindowType()
{
return "Wayland";
}
void WindowInfoK::update()
{
updateInternalId();
updateAppId();
updateIcon();
updateTitle();
updateGeometry();
updateDemandingAttention();
updateCloseable();
updateProcessInfo();
}
void WindowInfoK::killClient()
{
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WINDOWINFOK_H
#define WINDOWINFOK_H
#include "windowinfobase.h"
#include "dbusplasmawindow.h"
#include <QString>
class Entry;
class ProcessInfo;
// wayland下窗口信息
class WindowInfoK: public WindowInfoBase
{
public:
WindowInfoK(PlasmaWindow *window, XWindow _xid = 0);
virtual ~WindowInfoK();
virtual bool shouldSkip();
virtual QString getIcon();
virtual QString getTitle();
virtual bool isDemandingAttention();
virtual bool allowClose();
virtual void close(uint32_t timestamp);
virtual void activate();
virtual void minimize();
virtual bool isMinimized();
virtual int64_t getCreatedTime();
virtual QString getDisplayName();
virtual QString getWindowType();
virtual void update();
virtual void killClient();
QString getAppId();
void setAppId(QString _appId);
bool changeXid(XWindow _xid);
PlasmaWindow *getPlasmaWindow();
bool updateGeometry();
void updateTitle();
void updateDemandingAttention();
void updateIcon();
void updateAppId();
void updateInternalId();
void updateCloseable();
void updateProcessInfo();
private:
bool updateCalled;
QString appId;
uint32_t internalId;
bool demaningAttention;
bool closeable;
bool minimized;
PlasmaWindow *plasmaWindow;
Rect geometry;
};
#endif // WINDOWINFOK_H

View File

@ -0,0 +1,447 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "windowinfox.h"
#include "appinfo.h"
#include "xcbutils.h"
#include "dstring.h"
#include "common.h"
#include "processinfo.h"
#include <QDebug>
#include <QCryptographicHash>
#define XCB XCBUtils::instance()
WindowInfoX::WindowInfoX(XWindow _xid)
: WindowInfoBase ()
, x(0)
, y(0)
, width(0)
, height(0)
, hasWMTransientFor(false)
, hasXEmbedInfo(false)
, updateCalled(false)
{
xid = _xid;
createdTime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); // 获取当前时间,精确到纳秒
}
WindowInfoX::~WindowInfoX()
{
}
bool WindowInfoX::shouldSkip()
{
qInfo() << "window " << xid << " shouldSkip?";
if (!updateCalled) {
update();
updateCalled = true;
}
if (hasWmStateSkipTaskBar() || isValidModal() || shouldSkipWithWMClass())
return true;
for (auto atom : wmWindowType) {
if (atom == XCB->getAtom("_NET_WM_WINDOW_TYPE_DIALOG") && !isActionMinimizeAllowed())
return true;
if (atom == XCB->getAtom("_NET_WM_WINDOW_TYPE_UTILITY")
|| atom == XCB->getAtom("_NET_WM_WINDOW_TYPE_COMBO")
|| atom == XCB->getAtom("_NET_WM_WINDOW_TYPE_DESKTOP")
|| atom == XCB->getAtom("_NET_WM_WINDOW_TYPE_DND")
|| atom == XCB->getAtom("_NET_WM_WINDOW_TYPE_DOCK")
|| atom == XCB->getAtom("_NET_WM_WINDOW_TYPE_DROPDOWN_MENU")
|| atom == XCB->getAtom("_NET_WM_WINDOW_TYPE_MENU")
|| atom == XCB->getAtom("_NET_WM_WINDOW_TYPE_NOTIFICATION")
|| atom == XCB->getAtom("_NET_WM_WINDOW_TYPE_POPUP_MENU")
|| atom == XCB->getAtom("_NET_WM_WINDOW_TYPE_SPLASH")
|| atom == XCB->getAtom("_NET_WM_WINDOW_TYPE_TOOLBAR")
|| atom == XCB->getAtom("_NET_WM_WINDOW_TYPE_TOOLTIP"))
return true;
}
return false;
}
QString WindowInfoX::getIcon()
{
if (icon.isEmpty())
icon = getIconFromWindow();
return icon;
}
void WindowInfoX::activate()
{
XCB->setActiveWindow(xid);
}
void WindowInfoX::minimize()
{
XCB->minimizeWindow(xid);
}
bool WindowInfoX::isMinimized()
{
return containAtom(wmState, XCB->getAtom("_NET_WM_STATE_HIDDEN"));
}
int64_t WindowInfoX::getCreatedTime()
{
return createdTime;
}
QString WindowInfoX::getWindowType()
{
return "X11";
}
bool WindowInfoX::allowClose()
{
// 允许关闭的条件:
// 1. 不设置 functions 字段即MotifHintFunctions 标志位;
// 2. 或者设置了 functions 字段并且 设置了 MotifFunctionAll 标志位;
// 3. 或者设置了 functions 字段并且 设置了 MotifFunctionClose 标志位。
// 相关定义在 motif-2.3.8/lib/Xm/MwmUtil.h 。
if ((motifWmHints.flags & MotifHintFunctions) == 0
|| (motifWmHints.functions & MotifFunctionAll) != 0
|| (motifWmHints.functions & MotifFunctionClose) != 0)
return true;
for (auto action : wmAllowedActions) {
if (action == XCB->getAtom("_NET_WM_ACTION_CLOSE")) {
return true;
}
}
return false;
}
QString WindowInfoX::getDisplayName()
{
XWindow winId = xid;
//QString role = wmRole;
QString className(wmClass.className.c_str());
QString instance;
if (wmClass.instanceName.size() > 0) {
int pos = QString(wmClass.instanceName.c_str()).lastIndexOf('/');
if (pos != -1)
instance.remove(0, pos + 1);
}
qInfo() << "getDisplayName class:" << className << " ,instance:" << instance;
//if (!role.isEmpty() && !className.isEmpty())
// return className + " " + role;
if (!className.isEmpty())
return className;
if (!instance.isEmpty())
return instance;
QString _wmName = wmName;
if (!_wmName.isEmpty()) {
int pos = _wmName.lastIndexOf('-');
if (pos != -1 && !_wmName.startsWith("-")) {
_wmName.truncate(pos);
return _wmName;
}
}
if (processInfo) {
QString exe {processInfo->getEnv("exe").c_str()};
if (!exe.isEmpty())
return exe;
}
return QString("window:%1").arg(winId);
}
void WindowInfoX::killClient()
{
XCB->killClientChecked(xid);
}
QString WindowInfoX::getGtkAppId()
{
return gtkAppId;
}
QString WindowInfoX::getFlatpakAppId()
{
return flatpakAppId;
}
QString WindowInfoX::getWmRole()
{
return wmRole;
}
WMClass WindowInfoX::getWMClass()
{
return wmClass;
}
QString WindowInfoX::getWMName()
{
return wmName;
}
ConfigureEvent *WindowInfoX::getLastConfigureEvent()
{
return lastConfigureNotifyEvent;
}
void WindowInfoX::setLastConfigureEvent(ConfigureEvent *event)
{
lastConfigureNotifyEvent = event;
}
bool WindowInfoX::isGeometryChanged(int _x, int _y, int _width, int _height)
{
return !(_x == x && _y == y && _width == width && _height == height);
}
void WindowInfoX::setGtkAppId(QString _gtkAppId)
{
gtkAppId = _gtkAppId;
}
void WindowInfoX::updateMotifWmHints()
{
// get from XCB
motifWmHints = XCB->getWindowMotifWMHints(xid);
}
// XEmbed info
// 一般 tray icon 会带有 _XEMBED_INFO 属性
void WindowInfoX::updateHasXEmbedInfo()
{
hasXEmbedInfo = XCB->hasXEmbedInfo(xid);
}
QString WindowInfoX::genInnerId(WindowInfoX *winInfo)
{
XWindow winId = winInfo->getXid();
QString wmClassName, wmInstance;
WMClass wmClass = winInfo->getWMClass();
if (wmClass.className.size() > 0)
wmClassName = wmClass.className.c_str();
if (wmClass.instanceName.size() > 0) {
QString instanceName(wmClass.instanceName.c_str());
instanceName.remove(0, instanceName.lastIndexOf('/') + 1);
wmInstance = instanceName;
}
QString exe, args;
if (winInfo->getProcess()) {
exe = winInfo->getProcess()->getExe().c_str();
for (auto arg : winInfo->getProcess()->getArgs()) {
QString argStr(arg.c_str());
if (argStr.contains("/") || argStr == "." || argStr == "..") {
args += "%F ";
} else {
args += argStr + " ";
}
}
if (args.size() > 0)
args.remove(args.size() - 2, 1);
}
bool hasPid = winInfo->getPid() != 0;
QString str;
// NOTE: 不要使用 wmRole有些程序总会改变这个值比如 GVim
if (wmInstance.isEmpty() && wmClassName.isEmpty() && exe.isEmpty() && winInfo->getGtkAppId().isEmpty()) {
if (!winInfo->getWMName().isEmpty())
str = QString("wmName:%1").arg(winInfo->getWMName());
else
str = QString("windowId:%1").arg(winInfo->getXid());
} else {
str = QString("wmInstance:%1,wmClass:%2,exe:%3,args:%4,hasPid:%5,gtkAppId:%6").arg(wmInstance).arg(wmClassName).arg(exe).arg(args).arg(hasPid).arg(winInfo->getGtkAppId());
}
QByteArray encryText = QCryptographicHash::hash(str.toLatin1(), QCryptographicHash::Md5);
QString innerId = windowHashPrefix + encryText.toHex();
qInfo() << "genInnerId window " << winId << " innerId :" << innerId;
return innerId;
}
// 更新窗口类型
void WindowInfoX::updateWmWindowType()
{
wmWindowType.clear();
for (auto ty : XCB->getWMWindoType(xid)) {
wmWindowType.push_back(ty);
}
}
// 更新窗口许可动作
void WindowInfoX::updateWmAllowedActions()
{
wmAllowedActions.clear();
for (auto action : XCB->getWMAllowedActions(xid)) {
wmAllowedActions.push_back(action);
}
}
void WindowInfoX::updateWmState()
{
wmState.clear();
for (auto a : XCB->getWMState(xid)) {
wmState.push_back(a);
}
}
void WindowInfoX::updateWmClass()
{
wmClass = XCB->getWMClass(xid);
}
void WindowInfoX::updateWmName()
{
auto name = XCB->getWMName(xid);
if (!name.empty())
wmName = name.c_str();
title = getTitle();
}
void WindowInfoX::updateIcon()
{
icon = getIconFromWindow();
}
void WindowInfoX::updateHasWmTransientFor()
{
if (XCB->getWMTransientFor(xid) == 1)
hasWMTransientFor = true;
}
void WindowInfoX::update()
{
updateWmClass();
updateWmState();
updateWmWindowType();
updateWmAllowedActions();
updateHasWmTransientFor();
updateProcessInfo();
updateWmName();
innerId = genInnerId(this);
}
// TODO 从窗口中获取图标, 并设置best size be used in Entry
QString WindowInfoX::getIconFromWindow()
{
QString ret;
return ret;
}
bool WindowInfoX::isActionMinimizeAllowed()
{
return containAtom(wmAllowedActions, XCB->getAtom("_NET_WM_ACTION_MINIMIZE"));
}
bool WindowInfoX::hasWmStateDemandsAttention()
{
return containAtom(wmState, XCB->getAtom("_NET_WM_STATE_DEMANDS_ATTENTION"));
}
bool WindowInfoX::hasWmStateSkipTaskBar()
{
return containAtom(wmState, XCB->getAtom("_NET_WM_STATE_SKIP_TASKBAR"));
}
bool WindowInfoX::hasWmStateModal()
{
return containAtom(wmState, XCB->getAtom("_NET_WM_STATE_MODAL"));
}
bool WindowInfoX::isValidModal()
{
return hasWmStateModal() && hasWmStateModal();
}
// 通过WMClass判断是否需要隐藏此窗口
bool WindowInfoX::shouldSkipWithWMClass()
{
bool ret = false;
if (wmClass.instanceName == "explorer.exe" && wmClass.className == "Wine")
ret = true;
else if (wmClass.className == "dde-launcher")
ret = true;
return ret;
}
void WindowInfoX::updateProcessInfo()
{
XWindow winId = xid;
pid = XCB->getWMPid(winId);
if (processInfo)
delete processInfo;
processInfo = new ProcessInfo(pid);
if (!processInfo) {
// try WM_COMMAND
auto wmComand = XCB->getWMCommand(winId);
if (wmComand.size() > 0)
processInfo = new ProcessInfo(wmComand);
}
qInfo() << "updateProcessInfo: pid is " << pid;
}
bool WindowInfoX::getUpdateCalled()
{
return updateCalled;
}
void WindowInfoX::setInnerId(QString _innerId)
{
innerId = _innerId;
}
QString WindowInfoX::getTitle()
{
QString name = wmName;
if (name.isEmpty())
name = getDisplayName();
return name;
}
bool WindowInfoX::isDemandingAttention()
{
return hasWmStateDemandsAttention();
}
void WindowInfoX::close(uint32_t timestamp)
{
XCB->requestCloseWindow(xid, timestamp);
}

View File

@ -0,0 +1,108 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WINDOWINFOX_H
#define WINDOWINFOX_H
#include "windowinfobase.h"
#include "xcbutils.h"
#include <QVector>
class AppInfo;
// X11下窗口信息 在明确X11环境下使用
class WindowInfoX: public WindowInfoBase
{
public:
WindowInfoX(XWindow _xid = 0);
virtual ~WindowInfoX();
virtual bool shouldSkip();
virtual QString getIcon();
virtual QString getTitle();
virtual bool isDemandingAttention();
virtual void close(uint32_t timestamp);
virtual void activate();
virtual void minimize();
virtual bool isMinimized();
virtual int64_t getCreatedTime();
virtual QString getDisplayName();
virtual QString getWindowType();
virtual bool allowClose();
virtual void update();
virtual void killClient();
QString genInnerId(WindowInfoX *winInfo);
QString getGtkAppId();
QString getFlatpakAppId();
QString getWmRole();
WMClass getWMClass();
QString getWMName();
void updateProcessInfo();
bool getUpdateCalled();
void setInnerId(QString _innerId);
ConfigureEvent *getLastConfigureEvent();
void setLastConfigureEvent(ConfigureEvent *event);
bool isGeometryChanged(int _x, int _y, int _width, int _height);
void setGtkAppId(QString _gtkAppId);
/************************更新XCB窗口属性*********************/
void updateWmWindowType();
void updateWmAllowedActions();
void updateWmState();
void updateWmClass();
void updateMotifWmHints();
void updateWmName();
void updateIcon();
void updateHasXEmbedInfo();
void updateHasWmTransientFor();
private:
QString getIconFromWindow();
bool isActionMinimizeAllowed();
bool hasWmStateDemandsAttention();
bool hasWmStateSkipTaskBar();
bool hasWmStateModal();
bool isValidModal();
bool shouldSkipWithWMClass();
int16_t x, y;
uint16_t width, height;
QVector<XCBAtom> wmState;
QVector<XCBAtom> wmWindowType;
QVector<XCBAtom> wmAllowedActions;
bool hasWMTransientFor;
WMClass wmClass;
QString wmName;
bool hasXEmbedInfo;
// 自定义atom属性
QString gtkAppId;
QString flatpakAppId;
QString wmRole;
MotifWMHints motifWmHints;
bool updateCalled;
ConfigureEvent *lastConfigureNotifyEvent;
};
#endif // WINDOWINFOX_H

View File

@ -0,0 +1,229 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "windowpatterns.h"
#include "common.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonArray>
#include <QVariant>
#include <QVariantMap>
#include <QDebug>
#include <dstring.h>
#include <QRegExp>
#include <QFile>
const int parsedFlagNegative = 0x001;
const int parsedFlagIgnoreCase = 0x010;
bool contains(QString key, QString value) {
return key.contains(value);
}
bool containsIgnoreCase(QString key, QString value) {
QString _key = key.toLower();
QString _value = value.toLower();
return _key.contains(_value);
}
bool equal(QString key, QString value) {
return key == value;
}
bool equalIgnoreCase(QString key, QString value) {
return key.toLower() == value.toLower();
}
bool regexMatch(QString key, QString value) {
QRegExp ruleRegex(value);
return ruleRegex.exactMatch(key);
}
bool regexMatchIgnoreCase(QString key, QString value) {
QRegExp ruleRegex(value, Qt::CaseInsensitive);
return ruleRegex.exactMatch(key);
}
RuleValueParse::RuleValueParse()
: negative(0)
, type(0)
, flags(0)
{
}
bool RuleValueParse::parse(QString parsedKey)
{
if (!fn)
return false;
return negative ? fn(parsedKey, value) : !fn(parsedKey, value);
}
bool RuleValueParse::match(const WindowInfoX *winInfo)
{
QString parsedKey;
return parse(parsedKey);
}
WindowPatterns::WindowPatterns()
{
}
QString WindowPatterns::match(WindowInfoX *winInfo)
{
return "";
}
void WindowPatterns::loadWindowPatterns()
{
qInfo() << "---loadWindowPatterns";
QFile file(windowPatternsFile);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
file.close();
if (!doc.isArray())
return;
QJsonArray arr = doc.array();
if (arr.size() == 0)
return;
patterns.clear();
for (auto iterp = arr.begin(); iterp != arr.end(); iterp++) {
// 过滤非Object
if (!(*iterp).isObject())
continue;
QJsonObject patternObj = (*iterp).toObject();
QVariantMap patternMap = patternObj.toVariantMap();
WindowPattern pattern;
for (auto infoIter = patternMap.begin(); infoIter != patternMap.end(); infoIter++) {
QString ret = infoIter.key();
QVariant value = infoIter.value();
if (ret == "ret") {
pattern.result = value.toString();
} else if (ret == "rules") {
for (auto &item : value.toList()) {
if (!item.isValid())
continue;
if (item.toList().size() != 2)
continue;
pattern.rules.push_back({item.toList()[0].toString(), item.toList()[1].toString()});
}
}
}
qInfo() << pattern.result;
for (const auto &item : pattern.rules) {
qInfo() << item[0] << " " << item[1];
}
patterns.push_back(pattern);
}
// 解析patterns
for (auto &pattern : patterns) {
for (int i=0; i < pattern.rules.size(); i++) {
RuleValueParse ruleValue = parseRule(pattern.rules[i]);
pattern.parseRules.push_back(ruleValue);
}
}
}
// "=:XXX" equal XXX
// "=!XXX" not equal XXX
// "c:XXX" contains XXX
// "c!XXX" not contains XXX
// "r:XXX" match regexp XXX
// "r!XXX" not match regexp XXX
// e c r ignore case
// = E C R not ignore case
// 解析窗口类型规则
RuleValueParse WindowPatterns::parseRule(QVector<QString> rule)
{
RuleValueParse ret;
ret.key = rule[0];
ret.original = rule[1];
if (rule[1].size() < 2)
return ret;
int len = ret.original.size() + 1;
char *orig = static_cast<char *>(calloc(1, size_t(len)));
if (!orig)
return ret;
strncpy(orig, ret.original.toStdString().c_str(), size_t(len));
switch (orig[1]) {
case ':':
case '!':
ret.flags |= parsedFlagNegative;
ret.negative = true;
break;
default:
return ret;
}
ret.value = QString(&orig[2]);
ret.type = uint8_t(orig[0]);
switch (orig[0]) {
case 'C':
ret.fn = contains;
break;
case 'c':
ret.flags |= parsedFlagIgnoreCase;
ret.fn = containsIgnoreCase;
break;
case '=':
case 'E':
ret.fn = equal;
break;
case 'e':
ret.flags |= parsedFlagIgnoreCase;
ret.fn = equalIgnoreCase;
break;
case 'R':
ret.fn = regexMatch;
break;
case 'r':
ret.flags |= parsedFlagIgnoreCase;
ret.fn = regexMatchIgnoreCase;
break;
default:
return ret;
}
return ret;
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WINDOWPATTERNS_H
#define WINDOWPATTERNS_H
#include "windowinfox.h"
#include <QString>
#include <QVector>
struct RuleValueParse {
RuleValueParse();
bool parse(QString parsedKey);
bool match(const WindowInfoX *winInfo);
QString key;
bool negative;
bool (*fn)(QString, QString);
uint8_t type;
uint flags;
QString original;
QString value;
};
class WindowPatterns
{
// 窗口类型匹配
struct WindowPattern {
QVector<QVector<QString>> rules; // rules
QString result; // ret
QVector< RuleValueParse> parseRules;
};
public:
WindowPatterns();
QString match(WindowInfoX *winInfo);
private:
void loadWindowPatterns();
RuleValueParse parseRule(QVector<QString> rule);
QVector<WindowPattern> patterns;
};
#endif // WINDOWPATTERNS_H

View File

@ -0,0 +1,438 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "x11manager.h"
#include "dock.h"
#include "common.h"
#include <QDebug>
#include <QTimer>
/*
* 使用Xlib监听X Events
* 使用XCB接口与X进行交互
* */
#include <ctype.h>
#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xproto.h>
#define XCB XCBUtils::instance()
X11Manager::X11Manager(Dock *_dock, QObject *parent)
: QObject(parent)
, dock(_dock)
, mutex(QMutex(QMutex::NonRecursive))
, listenXEvent(true)
{
rootWindow = XCB->getRootWindow();
}
void X11Manager::listenXEventUseXlib()
{
Display *dpy;
int screen;
char *displayname = nullptr;
Window w;
XSetWindowAttributes attr;
XWindowAttributes wattr;
dpy = XOpenDisplay (displayname);
if (!dpy) {
exit (1);
}
screen = DefaultScreen (dpy);
w = RootWindow(dpy, screen);
const struct {
const char *name;
long mask;
} events[] = {
{ "keyboard", KeyPressMask | KeyReleaseMask | KeymapStateMask },
{ "mouse", ButtonPressMask | ButtonReleaseMask | EnterWindowMask |
LeaveWindowMask | PointerMotionMask | Button1MotionMask |
Button2MotionMask | Button3MotionMask | Button4MotionMask |
Button5MotionMask | ButtonMotionMask },
{ "button", ButtonPressMask | ButtonReleaseMask },
{ "expose", ExposureMask },
{ "visibility", VisibilityChangeMask },
{ "structure", StructureNotifyMask },
{ "substructure", SubstructureNotifyMask | SubstructureRedirectMask },
{ "focus", FocusChangeMask },
{ "property", PropertyChangeMask },
{ "colormap", ColormapChangeMask },
{ "owner_grab_button", OwnerGrabButtonMask },
{ nullptr, 0 }
};
long mask = 0;
for (int i = 0; events[i].name; i++)
mask |= events[i].mask;
attr.event_mask = mask;
XGetWindowAttributes(dpy, w, &wattr);
attr.event_mask &= ~SubstructureRedirectMask;
XSelectInput(dpy, w, attr.event_mask);
while (listenXEvent) {
XEvent event;
XNextEvent (dpy, &event);
if (event.type == DestroyNotify) {
XDestroyWindowEvent *eD = (XDestroyWindowEvent *) (&event);
//qInfo() << "DestroyNotify windowId=" << eD->window;
handleDestroyNotifyEvent(XWindow(eD->window));
} else if (event.type == MapNotify) {
XMapEvent *eM = (XMapEvent *)(&event);
//qInfo() << "MapNotify windowId=" << eM->window;
handleMapNotifyEvent(XWindow(eM->window));
} else if (event.type == ConfigureNotify ) {
XConfigureEvent *eC = (XConfigureEvent *) (&event);
//qInfo() << "ConfigureNotify windowId=" << eC->window;
handleConfigureNotifyEvent(XWindow(eC->window), eC->x, eC->y, eC->width, eC->height);
} else if (event.type == PropertyNotify) {
XPropertyEvent *eP = (XPropertyEvent *) (&event);
//qInfo() << "PropertyNotify windowId=" << eP->window;
handlePropertyNotifyEvent(XWindow(eP->window), XCBAtom(eP->atom));
} else {
//qInfo() << "Unknown event type " << event.type;
}
}
XCloseDisplay (dpy);
}
void X11Manager::listenXEventUseXCB()
{
/*
xcb_get_window_attributes_cookie_t cookie = xcb_get_window_attributes(XCB->getConnect(), XCB->getRootWindow());
xcb_get_window_attributes_reply_t *reply = xcb_get_window_attributes_reply(XCB->getConnect(), cookie, NULL);
if (reply) {
uint32_t valueMask = reply->your_event_mask;
valueMask &= ~XCB_CW_OVERRIDE_REDIRECT;
uint32_t mask[2] = {0};
mask[0] = valueMask;
//xcb_change_window_attributes(XCB->getConnect(), XCB->getRootWindow(), valueMask, mask);
free(reply);
}
xcb_generic_event_t *event;
while ( (event = xcb_wait_for_event (XCB->getConnect())) ) {
eventHandler(event->response_type & ~0x80, event);
}
*/
}
// 注册X11窗口
WindowInfoX *X11Manager::registerWindow(XWindow xid)
{
qInfo() << "registWindow: windowId=" << xid;
WindowInfoX *ret = nullptr;
do {
if (windowInfoMap.find(xid) != windowInfoMap.end()) {
ret = windowInfoMap[xid];
break;
}
WindowInfoX *winInfo = new WindowInfoX(xid);
if (!winInfo)
break;
listenWindowXEvent(winInfo);
windowInfoMap[xid] = winInfo;
ret = winInfo;
} while (0);
return ret;
}
// 取消注册X11窗口
void X11Manager::unregisterWindow(XWindow xid)
{
qInfo() << "unregisterWindow: windowId=" << xid;
if (windowInfoMap.find(xid) != windowInfoMap.end()) {
windowInfoMap.remove(xid);
}
}
WindowInfoX *X11Manager::findWindowByXid(XWindow xid)
{
WindowInfoX *ret = nullptr;
if (windowInfoMap.find(xid) != windowInfoMap.end())
ret = windowInfoMap[xid];
return ret;
}
void X11Manager::handleClientListChanged()
{
QSet<XWindow> newClientList, oldClientList, addClientList, rmClientList;
for (auto atom : XCB->getClientList())
newClientList.insert(atom);
for (auto atom : dock->getClientList())
oldClientList.insert(atom);
addClientList = newClientList - oldClientList;
rmClientList = oldClientList - newClientList;
for (auto xid : addClientList) {
WindowInfoX *info = registerWindow(xid);
if (!XCB->isGoodWindow(xid))
continue;
uint32_t pid = XCB->getWMPid(xid);
WMClass wmClass = XCB->getWMClass(xid);
QString wmName(XCB->getWMName(xid).c_str());
if (pid != 0 || (wmClass.className.size() > 0 && wmClass.instanceName.size() > 0)
|| wmName.size() > 0 || XCB->getWMCommand(xid).size() > 0)
dock->attachWindow(info);
}
for (auto xid : rmClientList) {
WindowInfoX *info = windowInfoMap[xid];
if (info) {
dock->detachWindow(info);
unregisterWindow(xid);
} else {
// no window
auto entry = dock->getEntryByWindowId(xid);
if (entry && !dock->isDocked(entry->getFileName())) {
dock->removeAppEntry(entry);
}
}
}
}
void X11Manager::handleActiveWindowChangedX()
{
XWindow active = XCB->getActiveWindow();
WindowInfoX *info = findWindowByXid(active);
dock->handleActiveWindowChanged(info);
}
void X11Manager::listenRootWindowXEvent()
{
uint32_t eventMask = EventMask::XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
XCB->registerEvents(rootWindow, eventMask);
handleActiveWindowChangedX();
handleClientListChanged();
}
void X11Manager::listenWindowXEvent(WindowInfoX *winInfo)
{
uint32_t eventMask = EventMask::XCB_EVENT_MASK_PROPERTY_CHANGE | EventMask::XCB_EVENT_MASK_STRUCTURE_NOTIFY | EventMask::XCB_EVENT_MASK_VISIBILITY_CHANGE;
XCB->registerEvents(winInfo->getXid(), eventMask);
}
void X11Manager::handleRootWindowPropertyNotifyEvent(XCBAtom atom)
{
if (atom == XCB->getAtom("_NET_CLIENT_LIST"))
handleClientListChanged();
else if (atom == XCB->getAtom("_NET_ACTIVE_WINDOW"))
handleActiveWindowChangedX();
else if (atom == XCB->getAtom("_NET_SHOWING_DESKTOP"))
Q_EMIT needUpdateHideState(false);
}
// destory event
void X11Manager::handleDestroyNotifyEvent(XWindow xid)
{
WindowInfoX *winInfo = findWindowByXid(xid);
if (!winInfo)
return;
dock->detachWindow(winInfo);
unregisterWindow(xid);
}
// map event
void X11Manager::handleMapNotifyEvent(XWindow xid)
{
WindowInfoX *winInfo = registerWindow(xid);
if (!winInfo)
return;
QTimer::singleShot(2 * 1000, this, [&] {
qInfo() << "handleMapNotifyEvent: pass 2s, now call idnetifyWindow, windowId=" << winInfo->getXid();
QString innerId;
AppInfo *appInfo = dock->identifyWindow(winInfo, innerId);
dock->markAppLaunched(appInfo);
});
}
// config changed event 检测窗口大小调整和重绘应用
void X11Manager::handleConfigureNotifyEvent(XWindow xid, int x, int y, int width, int height)
{
WindowInfoX *winInfo = findWindowByXid(xid);
if (!winInfo || dock->getDockHideMode() != HideMode::SmartHide)
return;
WMClass wmClass = winInfo->getWMClass();
if (wmClass.className.c_str() == frontendWindowWmClass)
return; // ignore frontend window ConfigureNotify event
Q_EMIT needUpdateHideState(winInfo->isGeometryChanged(x, y, width, height));
}
// property changed event
void X11Manager::handlePropertyNotifyEvent(XWindow xid, XCBAtom atom)
{
if (xid == rootWindow) {
handleRootWindowPropertyNotifyEvent(atom);
return;
}
WindowInfoX *winInfo = findWindowByXid(xid);
if (!winInfo)
return;
QString newInnerId;
bool needAttachOrDetach = false;
if (atom == XCB->getAtom("_NET_WM_STATE")) {
winInfo->updateWmState();
needAttachOrDetach = true;
} else if (atom == XCB->getAtom("_GTK_APPLICATION_ID")) {
QString gtkAppId;
winInfo->setGtkAppId(gtkAppId);
newInnerId = winInfo->genInnerId(winInfo);
} else if (atom == XCB->getAtom("_NET_WM_PID")) {
winInfo->updateProcessInfo();
newInnerId = winInfo->genInnerId(winInfo);
} else if (atom == XCB->getAtom("_NET_WM_NAME")) {
winInfo->updateWmName();
newInnerId = winInfo->genInnerId(winInfo);
} else if (atom == XCB->getAtom("_NET_WM_ICON")) {
winInfo->updateIcon();
} else if (atom == XCB->getAtom("_NET_WM_ALLOWED_ACTIONS")) {
winInfo->updateWmAllowedActions();
} else if (atom == XCB->getAtom("_MOTIF_WM_HINTS")) {
winInfo->updateMotifWmHints();
} else if (atom == XCB_ATOM_WM_CLASS) {
winInfo->updateWmClass();
newInnerId = winInfo->genInnerId(winInfo);
needAttachOrDetach = true;
} else if (atom == XCB->getAtom("_XEMBED_INFO")) {
winInfo->updateHasXEmbedInfo();
needAttachOrDetach = true;
} else if (atom == XCB->getAtom("_NET_WM_WINDOW_TYPE")) {
winInfo->updateWmWindowType();
needAttachOrDetach = true;
} else if (atom == XCB_ATOM_WM_TRANSIENT_FOR) {
winInfo->updateHasWmTransientFor();
needAttachOrDetach = true;
}
if (!newInnerId.isEmpty() && winInfo->getUpdateCalled() && winInfo->getInnerId() != newInnerId) {
// winInfo.innerId changed
dock->detachWindow(winInfo);
winInfo->setInnerId(newInnerId);
needAttachOrDetach = true;
}
if (needAttachOrDetach)
dock->attachWindow(winInfo);
Entry *entry = dock->getEntryByWindowId(xid);
if (!entry)
return;
if (atom == XCB->getAtom("_NET_WM_STATE")) {
entry->updateWindowInfos();
} else if (atom == XCB->getAtom("_NET_WM_ICON")) {
if (entry->getCurrentWindowInfo() == winInfo) {
entry->updateIcon();
}
} else if (atom == XCB->getAtom("_NET_WM_NAME")) {
if (entry->getCurrentWindowInfo() == winInfo) {
entry->updateName();
}
entry->updateWindowInfos();
} else if (atom == XCB->getAtom("_NET_WM_ALLOWED_ACTIONS")) {
entry->updateMenu();
}
}
void X11Manager::eventHandler(uint8_t type, void *event)
{
qInfo() << "eventHandler" << "type = " << type;
switch (type) {
case XCB_MAP_NOTIFY: // 17 注册新窗口
qInfo() << "eventHandler: XCB_MAP_NOTIFY";
break;
case XCB_DESTROY_NOTIFY: // 19 销毁窗口
qInfo() << "eventHandler: XCB_DESTROY_NOTIFY";
break;
case XCB_CONFIGURE_NOTIFY: // 22 窗口变化
qInfo() << "eventHandler: XCB_CONFIGURE_NOTIFY";
break;
case XCB_PROPERTY_NOTIFY: // 28 窗口属性改变
qInfo() << "eventHandler: XCB_PROPERTY_NOTIFY";
break;
}
}
void X11Manager::addWindowLastConfigureEvent(XWindow xid, ConfigureEvent *event)
{
delWindowLastConfigureEvent(xid);
QMutexLocker locker(&mutex);
QTimer *timer = new QTimer();
timer->setInterval(configureNotifyDelay);
windowLastConfigureEventMap[xid] = QPair(event, timer);
}
QPair<ConfigureEvent *, QTimer *> X11Manager::getWindowLastConfigureEvent(XWindow xid)
{
QPair<ConfigureEvent *, QTimer *> ret;
QMutexLocker locker(&mutex);
if (windowLastConfigureEventMap.find(xid) != windowLastConfigureEventMap.end())
ret = windowLastConfigureEventMap[xid];
return ret;
}
void X11Manager::delWindowLastConfigureEvent(XWindow xid)
{
QMutexLocker locker(&mutex);
if (windowLastConfigureEventMap.find(xid) != windowLastConfigureEventMap.end()) {
QPair<ConfigureEvent*, QTimer*> item = windowLastConfigureEventMap[xid];
windowLastConfigureEventMap.remove(xid);
delete item.first;
item.second->deleteLater();
}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef X11MANAGER_H
#define X11MANAGER_H
#include "windowinfox.h"
#include "xcbutils.h"
#include <QObject>
#include <QMap>
#include <QMutex>
#include <QTimer>
class Dock;
class X11Manager : public QObject
{
Q_OBJECT
public:
explicit X11Manager(Dock *_dock, QObject *parent = nullptr);
WindowInfoX *findWindowByXid(XWindow xid);
WindowInfoX *registerWindow(XWindow xid);
void unregisterWindow(XWindow xid);
void handleClientListChanged();
void handleActiveWindowChangedX();
void listenRootWindowXEvent();
void listenWindowXEvent(WindowInfoX *winInfo);
void handleRootWindowPropertyNotifyEvent(XCBAtom atom);
void handleDestroyNotifyEvent(XWindow xid);
void handleMapNotifyEvent(XWindow xid);
void handleConfigureNotifyEvent(XWindow xid, int x, int y, int width, int height);
void handlePropertyNotifyEvent(XWindow xid, XCBAtom atom);
void eventHandler(uint8_t type, void *event);
void listenWindowEvent(WindowInfoX *winInfo);
void listenXEventUseXlib();
void listenXEventUseXCB();
signals:
void needUpdateHideState(bool delay);
private:
void addWindowLastConfigureEvent(XWindow xid, ConfigureEvent* event);
QPair<ConfigureEvent*, QTimer*> getWindowLastConfigureEvent(XWindow xid);
void delWindowLastConfigureEvent(XWindow xid);
QMap<XWindow, WindowInfoX *> windowInfoMap;
Dock *dock;
QMap<XWindow, QPair<ConfigureEvent*, QTimer*>> windowLastConfigureEventMap; // 手动回收ConfigureEvent和QTimer
QMutex mutex;
XWindow rootWindow; // 根窗口
bool listenXEvent; // 监听X事件
};
#endif // X11MANAGER_H

View File

@ -0,0 +1,291 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "category.h"
Category::Category()
{
}
Category::~Category()
{
}
QString Category::getStr(Categorytype ty) {
QMap<Categorytype, QString> ty2str = {
{Categorytype::CategoryInternet, "Internet"},
{Categorytype::CategoryChat, "Chat"},
{Categorytype::CategoryMusic, "Music"},
{Categorytype::CategoryVideo, "Video"},
{Categorytype::CategoryGraphics, "Graphics"},
{Categorytype::CategoryOffice, "Office"},
{Categorytype::CategoryGame, "Game"},
{Categorytype::CategoryReading, "Reading"},
{Categorytype::CategoryDevelopment, "Development"},
{Categorytype::CategorySystem, "System"},
{Categorytype::CategoryOthers, "Others"},
};
return ty2str.find(ty) != ty2str.end() ? ty2str[ty] : "Others";
}
QString Category::pinYin(Categorytype ty) {
QMap<Categorytype, QString> ty2py = {
{Categorytype::CategoryInternet, "wangluo"},
{Categorytype::CategoryChat, "shejiaogoutong"},
{Categorytype::CategoryMusic, "yinyuexinshang"},
{Categorytype::CategoryVideo, "shipinbofang"},
{Categorytype::CategoryGraphics, "tuxintuxiang"},
{Categorytype::CategoryOffice, "bangongxuexi"},
{Categorytype::CategoryGame, "youxiyule"},
{Categorytype::CategoryReading, "yuedufanyi"},
{Categorytype::CategoryDevelopment, "bianchengkaifai"},
{Categorytype::CategorySystem, "xitongguanli"},
{Categorytype::CategoryOthers, "qita"},
};
return ty2py.find(ty) != ty2py.end() ? ty2py[ty] : "qita";
}
Categorytype Category::parseCategoryString(QString str) {
QMap<QString, Categorytype> name2ty {
{"internet", Categorytype::CategoryInternet},
{"chat", Categorytype::CategoryChat},
{"music", Categorytype::CategoryMusic},
{"video", Categorytype::CategoryVideo},
{"graphics", Categorytype::CategoryGraphics},
{"office", Categorytype::CategoryOffice},
{"game", Categorytype::CategoryGame},
{"reading", Categorytype::CategoryReading},
{"development", Categorytype::CategoryDevelopment},
{"system", Categorytype::CategorySystem},
{"others", Categorytype::CategoryOthers},
};
return name2ty.find(str) != name2ty.end() ? name2ty[str] : Categorytype::CategoryErr;
}
QList<Categorytype> Category::parseXCategoryString(QString str) {
QMultiMap<QString, Categorytype> xname2ty {
{"2dgraphics", Categorytype::CategoryGraphics},
{"3dgraphics", Categorytype::CategoryGraphics},
{"accessibility", Categorytype::CategorySystem},
{"accessories", Categorytype::CategoryOthers},
{"actiongame", Categorytype::CategoryGame},
{"advancedsettings", Categorytype::CategorySystem},
{"adventuregame", Categorytype::CategoryGame},
{"amusement", Categorytype::CategoryGame},
{"applet", Categorytype::CategoryOthers},
{"arcadegame", Categorytype::CategoryGame},
{"archiving", Categorytype::CategorySystem},
{"art", Categorytype::CategoryOffice},
{"artificialintelligence", Categorytype::CategoryOffice},
{"astronomy", Categorytype::CategoryOffice},
{"audio", Categorytype::CategoryMusic},
{"audiovideo", Categorytype::CategoryMusic},
{"audiovideo", Categorytype::CategoryVideo},
{"audiovideoediting", Categorytype::CategoryMusic},
{"audiovideoediting", Categorytype::CategoryVideo},
{"biology", Categorytype::CategoryOffice},
{"blocksgame", Categorytype::CategoryGame},
{"boardgame", Categorytype::CategoryGame},
{"building", Categorytype::CategoryDevelopment},
{"calculator", Categorytype::CategorySystem},
{"calendar", Categorytype::CategorySystem},
{"cardgame", Categorytype::CategoryGame},
{"cd", Categorytype::CategoryMusic},
{"chart", Categorytype::CategoryOffice},
{"chat", Categorytype::CategoryChat},
{"chemistry", Categorytype::CategoryOffice},
{"clock", Categorytype::CategorySystem},
{"compiz", Categorytype::CategorySystem},
{"compression", Categorytype::CategorySystem},
{"computerscience", Categorytype::CategoryOffice},
{"consoleonly", Categorytype::CategoryOthers},
{"contactmanagement", Categorytype::CategoryChat},
{"core", Categorytype::CategoryOthers},
{"debugger", Categorytype::CategoryDevelopment},
{"desktopsettings", Categorytype::CategorySystem},
{"desktoputility", Categorytype::CategorySystem},
{"development", Categorytype::CategoryDevelopment},
{"dialup", Categorytype::CategorySystem},
{"dictionary", Categorytype::CategoryOffice},
{"discburning", Categorytype::CategorySystem},
{"documentation", Categorytype::CategoryOffice},
{"editors", Categorytype::CategoryOthers},
{"education", Categorytype::CategoryOffice},
{"electricity", Categorytype::CategoryOffice},
{"electronics", Categorytype::CategoryOffice},
{"email", Categorytype::CategoryInternet},
{"emulator", Categorytype::CategoryGame},
{"engineering", Categorytype::CategorySystem},
{"favorites", Categorytype::CategoryOthers},
{"filemanager", Categorytype::CategorySystem},
{"filesystem", Categorytype::CategorySystem},
{"filetools", Categorytype::CategorySystem},
{"filetransfer", Categorytype::CategoryInternet},
{"finance", Categorytype::CategoryOffice},
{"game", Categorytype::CategoryGame},
{"geography", Categorytype::CategoryOffice},
{"geology", Categorytype::CategoryOffice},
{"geoscience", Categorytype::CategoryOthers},
{"gnome", Categorytype::CategorySystem},
{"gpe", Categorytype::CategoryOthers},
{"graphics", Categorytype::CategoryGraphics},
{"guidesigner", Categorytype::CategoryDevelopment},
{"hamradio", Categorytype::CategoryOffice},
{"hardwaresettings", Categorytype::CategorySystem},
{"ide", Categorytype::CategoryDevelopment},
{"imageprocessing", Categorytype::CategoryGraphics},
{"instantmessaging", Categorytype::CategoryChat},
{"internet", Categorytype::CategoryInternet},
{"ircclient", Categorytype::CategoryChat},
{"kde", Categorytype::CategorySystem},
{"kidsgame", Categorytype::CategoryGame},
{"literature", Categorytype::CategoryOffice},
{"logicgame", Categorytype::CategoryGame},
{"math", Categorytype::CategoryOffice},
{"medicalsoftware", Categorytype::CategoryOffice},
{"meteorology", Categorytype::CategoryOthers},
{"midi", Categorytype::CategoryMusic},
{"mixer", Categorytype::CategoryMusic},
{"monitor", Categorytype::CategorySystem},
{"motif", Categorytype::CategoryOthers},
{"multimedia", Categorytype::CategoryVideo},
{"music", Categorytype::CategoryMusic},
{"network", Categorytype::CategoryInternet},
{"news", Categorytype::CategoryReading},
{"numericalanalysis", Categorytype::CategoryOffice},
{"ocr", Categorytype::CategoryGraphics},
{"office", Categorytype::CategoryOffice},
{"p2p", Categorytype::CategoryInternet},
{"packagemanager", Categorytype::CategorySystem},
{"panel", Categorytype::CategorySystem},
{"pda", Categorytype::CategorySystem},
{"photography", Categorytype::CategoryGraphics},
{"physics", Categorytype::CategoryOffice},
{"pim", Categorytype::CategoryOthers},
{"player", Categorytype::CategoryMusic},
{"player", Categorytype::CategoryVideo},
{"playonlinux", Categorytype::CategoryOthers},
{"presentation", Categorytype::CategoryOffice},
{"printing", Categorytype::CategoryOffice},
{"profiling", Categorytype::CategoryDevelopment},
{"projectmanagement", Categorytype::CategoryOffice},
{"publishing", Categorytype::CategoryOffice},
{"puzzlegame", Categorytype::CategoryGame},
{"rastergraphics", Categorytype::CategoryGraphics},
{"recorder", Categorytype::CategoryMusic},
{"recorder", Categorytype::CategoryVideo},
{"remoteaccess", Categorytype::CategorySystem},
{"revisioncontrol", Categorytype::CategoryDevelopment},
{"robotics", Categorytype::CategoryOffice},
{"roleplaying", Categorytype::CategoryGame},
{"scanning", Categorytype::CategoryOffice},
{"science", Categorytype::CategoryOffice},
{"screensaver", Categorytype::CategoryOthers},
{"sequencer", Categorytype::CategoryMusic},
{"settings", Categorytype::CategorySystem},
{"security", Categorytype::CategorySystem},
{"simulation", Categorytype::CategoryGame},
{"sportsgame", Categorytype::CategoryGame},
{"spreadsheet", Categorytype::CategoryOffice},
{"strategygame", Categorytype::CategoryGame},
{"system", Categorytype::CategorySystem},
{"systemsettings", Categorytype::CategorySystem},
{"technical", Categorytype::CategoryOthers},
{"telephony", Categorytype::CategorySystem},
{"telephonytools", Categorytype::CategorySystem},
{"terminalemulator", Categorytype::CategorySystem},
{"texteditor", Categorytype::CategoryOffice},
{"texttools", Categorytype::CategoryOffice},
{"transiation", Categorytype::CategoryDevelopment},
{"translation", Categorytype::CategoryReading},
{"trayicon", Categorytype::CategorySystem},
{"tuner", Categorytype::CategoryMusic},
{"tv", Categorytype::CategoryVideo},
{"utility", Categorytype::CategorySystem},
{"vectorgraphics", Categorytype::CategoryGraphics},
{"video", Categorytype::CategoryVideo},
{"videoconference", Categorytype::CategoryInternet},
{"viewer", Categorytype::CategoryGraphics},
{"webbrowser", Categorytype::CategoryInternet},
{"webdevelopment", Categorytype::CategoryDevelopment},
{"wine", Categorytype::CategoryOthers},
{"wine-programs-accessories", Categorytype::CategoryOthers},
{"wordprocessor", Categorytype::CategoryOffice},
{"x-alsa", Categorytype::CategoryMusic},
{"x-bible", Categorytype::CategoryReading},
{"x-bluetooth", Categorytype::CategorySystem},
{"x-debian-applications-emulators", Categorytype::CategoryGame},
{"x-digital_processing", Categorytype::CategorySystem},
{"x-enlightenment", Categorytype::CategorySystem},
{"x-geeqie", Categorytype::CategoryGraphics},
{"x-gnome-networksettings", Categorytype::CategorySystem},
{"x-gnome-personalsettings", Categorytype::CategorySystem},
{"x-gnome-settings-panel", Categorytype::CategorySystem},
{"x-gnome-systemsettings", Categorytype::CategorySystem},
{"x-gnustep", Categorytype::CategorySystem},
{"x-islamic-software", Categorytype::CategoryReading},
{"x-jack", Categorytype::CategoryMusic},
{"x-kde-edu-misc", Categorytype::CategoryReading},
{"x-kde-internet", Categorytype::CategorySystem},
{"x-kde-more", Categorytype::CategorySystem},
{"x-kde-utilities-desktop", Categorytype::CategorySystem},
{"x-kde-utilities-file", Categorytype::CategorySystem},
{"x-kde-utilities-peripherals", Categorytype::CategorySystem},
{"x-kde-utilities-pim", Categorytype::CategorySystem},
{"x-lxde-settings", Categorytype::CategorySystem},
{"x-mandriva-office-publishing", Categorytype::CategoryOthers},
{"x-mandrivalinux-internet-other", Categorytype::CategorySystem},
{"x-mandrivalinux-office-other", Categorytype::CategoryOffice},
{"x-mandrivalinux-system-archiving-backup", Categorytype::CategorySystem},
{"x-midi", Categorytype::CategoryMusic},
{"x-misc", Categorytype::CategorySystem},
{"x-multitrack", Categorytype::CategoryMusic},
{"x-novell-main", Categorytype::CategorySystem},
{"x-quran", Categorytype::CategoryReading},
{"x-red-hat-base", Categorytype::CategorySystem},
{"x-red-hat-base-only", Categorytype::CategorySystem},
{"x-red-hat-extra", Categorytype::CategorySystem},
{"x-red-hat-serverconfig", Categorytype::CategorySystem},
{"x-religion", Categorytype::CategoryReading},
{"x-sequencers", Categorytype::CategoryMusic},
{"x-sound", Categorytype::CategoryMusic},
{"x-sun-supported", Categorytype::CategorySystem},
{"x-suse-backup", Categorytype::CategorySystem},
{"x-suse-controlcenter-lookandfeel", Categorytype::CategorySystem},
{"x-suse-controlcenter-system", Categorytype::CategorySystem},
{"x-suse-core", Categorytype::CategorySystem},
{"x-suse-core-game", Categorytype::CategoryGame},
{"x-suse-core-office", Categorytype::CategoryOffice},
{"x-suse-sequencer", Categorytype::CategoryMusic},
{"x-suse-yast", Categorytype::CategorySystem},
{"x-suse-yast-high_availability", Categorytype::CategorySystem},
{"x-synthesis", Categorytype::CategorySystem},
{"x-turbolinux-office", Categorytype::CategoryOffice},
{"x-xfce", Categorytype::CategorySystem},
{"x-xfce-toplevel", Categorytype::CategorySystem},
{"x-xfcesettingsdialog", Categorytype::CategorySystem},
{"x-ximian-main", Categorytype::CategorySystem},
};
return {xname2ty.values(str)};
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CATEGORY_H
#define CATEGORY_H
#include "common.h"
#include <QString>
#include <QMultiMap>
// 应用类型
enum class Categorytype {
CategoryInternet,
CategoryChat,
CategoryMusic,
CategoryVideo,
CategoryGraphics,
CategoryGame,
CategoryOffice,
CategoryReading,
CategoryDevelopment,
CategorySystem,
CategoryOthers,
CategoryErr,
};
class Category
{
public:
Category();
~Category();
// 类型转字符串
static QString getStr(Categorytype ty);
// 类型转拼音
static QString pinYin(Categorytype ty);
// 字符串转类型
static Categorytype parseCategoryString(QString str);
// Xorg类型字符串转类型列表
static QList<Categorytype> parseXCategoryString(QString str);
};
#endif // CATEGORY_H

View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COMMON_H
#define COMMON_H
#include <QString>
#include <QMultiMap>
// DBus服务、路径名
const QString dbusService = "org.deepin.dde.daemon.Launcher1";
const QString dbusPath = "/org/deepin/dde/daemon/Launcher1";
// 组策略配置
const QString configLauncher = "com.deepin.dde.launcher";
const QString keyDisplayMode = "Display_Mode";
const QString keyFullscreen = "Fullscreen";
const QString keyAppsUseProxy = "Apps_Use_Proxy";
const QString keyAppsDisableScaling = "Apps_Disable_Scaling";
const QString keyAppsHidden = "Apps_Hidden";
const QString keyPackageNameSearch = "Search_Package_Name";
// 应用配置
const QString lastoreDataDir = "/var/lib/lastore";
const QString desktopPkgMapFile = lastoreDataDir + "/desktop_package.json";
const QString applicationsFile = lastoreDataDir + "/applications.json";
const QString ddeDataDir = "/usr/share/dde/data/";
const QString appNameTranslationsFile = ddeDataDir + "app_name_translations.json";
// 应用状态
const QString appStatusCreated = "created";
const QString appStatusModified = "updated";
const QString appStatusDeleted = "deleted";
// flatpak应用
const QString flatpakBin = "flatpak";
// 启动器执行程序
const QString launcherExe = "/usr/bin/dde-launcher";
#endif // COMMON_H

View File

@ -0,0 +1,126 @@
#include "dbusadaptorlauncher.h"
DBusAdaptorLauncher::DBusAdaptorLauncher(QObject *parent)
: QDBusAbstractAdaptor(parent)
{
// constructor
setAutoRelaySignals(true);
Launcher *launcher = static_cast<Launcher *>(QObject::parent());
if (launcher) {
connect(launcher, &Launcher::displayModeChanged, this, &DBusAdaptorLauncher::DisplayModeChanged);
connect(launcher, &Launcher::fullScreenChanged, this, &DBusAdaptorLauncher::FullScreenChanged);
}
}
DBusAdaptorLauncher::~DBusAdaptorLauncher()
{
// destructor
}
Launcher *DBusAdaptorLauncher::parent() const
{
return static_cast<Launcher *>(QObject::parent());
}
int DBusAdaptorLauncher::displayMode() const
{
return parent()->getDisplayMode();
}
void DBusAdaptorLauncher::setDisplayMode(int value)
{
parent()->setDisplayMode(value);
}
bool DBusAdaptorLauncher::fullscreen() const
{
return parent()->getFullScreen();
}
void DBusAdaptorLauncher::setFullscreen(bool value)
{
parent()->setFullScreen(value);
}
bool DBusAdaptorLauncher::AddAutostart(const QString &desktopFile)
{
QDBusInterface interface = QDBusInterface("com.deepin.daemon.Display", "/com/deepin/StartManager", "com.deepin.StartManager");
QDBusReply<bool> reply = interface.call("AddAutostart", desktopFile);
return reply.isValid() ? reply.value() : false;
}
// TODO
QString DBusAdaptorLauncher::GetAllItemInfos()
{
// return parent()->getAllItemInfos();
return "";
}
QStringList DBusAdaptorLauncher::GetAllNewInstalledApps()
{
return parent()->getAllNewInstalledApps();
}
bool DBusAdaptorLauncher::GetDisableScaling(const QString &id)
{
return parent()->getDisableScaling(id);
}
// TODO
QString DBusAdaptorLauncher::GetItemInfo(const QString &id)
{
//return parent()->getItemInfo();
return "";
}
bool DBusAdaptorLauncher::GetUseProxy(const QString &id)
{
return parent()->getUseProxy(id);
}
bool DBusAdaptorLauncher::IsItemOnDesktop(const QString &id)
{
return parent()->isItemOnDesktop(id);
}
bool DBusAdaptorLauncher::LaunchWithTimestamp(const QString &desktopFile, int time)
{
QDBusInterface interface = QDBusInterface("com.deepin.daemon.Display", "/com/deepin/StartManager", "com.deepin.StartManager");
QDBusReply<bool> reply = interface.call("LaunchWithTimestamp", desktopFile, time);
return reply.isValid() ? reply.value() : false;
}
bool DBusAdaptorLauncher::RemoveAutostart(const QString &desktopFile)
{
QDBusInterface interface = QDBusInterface("com.deepin.daemon.Display", "/com/deepin/StartManager", "com.deepin.StartManager");
QDBusReply<bool> reply = interface.call("RemoveAutostart", desktopFile);
return reply.isValid() ? reply.value() : false;
}
bool DBusAdaptorLauncher::RequestRemoveFromDesktop(const QString &id)
{
return parent()->requestRemoveFromDesktop(id);
}
bool DBusAdaptorLauncher::RequestSendToDesktop(const QString &id)
{
return parent()->requestSendToDesktop(id);
}
void DBusAdaptorLauncher::RequestUninstall(const QString &id)
{
parent()->requestUninstall(id);
}
void DBusAdaptorLauncher::SetDisableScaling(const QString &id, bool value)
{
parent()->setDisableScaling(id, value);
}
void DBusAdaptorLauncher::SetUseProxy(const QString &id, bool value)
{
parent()->setUseProxy(id, value);
}

View File

@ -0,0 +1,139 @@
#ifndef DBUSADAPTORLAUNCHER_H
#define DBUSADAPTORLAUNCHER_H
#include "launcher.h"
#include <QtCore/QObject>
#include <QtCore/QMetaObject>
#include <QtCore/QVariant>
#include <QtDBus/QtDBus>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
/*
* Adaptor class for interface org.deepin.dde.daemon.Launcher1
*/
class DBusAdaptorLauncher: public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.deepin.dde.daemon.Launcher1")
Q_CLASSINFO("D-Bus Introspection", ""
" <interface name=\"org.deepin.dde.daemon.Launcher1\">\n"
" <method name=\"GetAllItemInfos\">\n"
" <arg direction=\"out\" type=\"s\" name=\"itemInfoList\"/>\n"
" </method>\n"
" <method name=\"GetAllNewInstalledApps\">\n"
" <arg direction=\"out\" type=\"as\" name=\"apps\"/>\n"
" </method>\n"
" <method name=\"GetDisableScaling\">\n"
" <arg direction=\"in\" type=\"s\" name=\"id\"/>\n"
" <arg direction=\"out\" type=\"b\" name=\"value\"/>\n"
" </method>\n"
" <method name=\"GetItemInfo\">\n"
" <arg direction=\"in\" type=\"s\" name=\"id\"/>\n"
" <arg direction=\"out\" type=\"s\" name=\"itemInfo\"/>\n"
" </method>\n"
" <method name=\"GetUseProxy\">\n"
" <arg direction=\"in\" type=\"s\" name=\"id\"/>\n"
" <arg direction=\"out\" type=\"b\" name=\"value\"/>\n"
" </method>\n"
" <method name=\"IsItemOnDesktop\">\n"
" <arg direction=\"in\" type=\"s\" name=\"id\"/>\n"
" <arg direction=\"out\" type=\"b\" name=\"result\"/>\n"
" </method>\n"
" <method name=\"RequestRemoveFromDesktop\">\n"
" <arg direction=\"in\" type=\"s\" name=\"id\"/>\n"
" <arg direction=\"out\" type=\"b\" name=\"ok\"/>\n"
" </method>\n"
" <method name=\"RequestSendToDesktop\">\n"
" <arg direction=\"in\" type=\"s\" name=\"id\"/>\n"
" <arg direction=\"out\" type=\"b\" name=\"ok\"/>\n"
" </method>\n"
" <method name=\"RequestUninstall\">\n"
" <arg direction=\"in\" type=\"s\" name=\"id\"/>\n"
" </method>\n"
" <method name=\"SetDisableScaling\">\n"
" <arg direction=\"in\" type=\"s\" name=\"id\"/>\n"
" <arg direction=\"in\" type=\"b\" name=\"value\"/>\n"
" </method>\n"
" <method name=\"SetUseProxy\">\n"
" <arg direction=\"in\" type=\"s\" name=\"id\"/>\n"
" <arg direction=\"in\" type=\"b\" name=\"value\"/>\n"
" </method>\n"
" <method name=\"LaunchWithTimestamp\">\n"
" <arg direction=\"in\" type=\"s\" name=\"desktopFile\"/>\n"
" <arg direction=\"in\" type=\"i\" name=\"time\"/>\n"
" <arg direction=\"out\" type=\"b\" name=\"value\"/>\n"
" </method>\n"
" <method name=\"RemoveAutostart\">\n"
" <arg direction=\"in\" type=\"s\" name=\"desktopFile\"/>\n"
" <arg direction=\"out\" type=\"b\" name=\"value\"/>\n"
" </method>\n"
" <method name=\"AddAutostart\">\n"
" <arg direction=\"in\" type=\"s\" name=\"desktopFile\"/>\n"
" <arg direction=\"out\" type=\"b\" name=\"value\"/>\n"
" </method>\n"
" <signal name=\"ItemChanged\">\n"
" <arg type=\"s\" name=\"status\"/>\n"
" <arg type=\"s\" name=\"itemInfo\"/>\n"
" <arg type=\"x\" name=\"categoryID\"/>\n"
" </signal>\n"
" <signal name=\"NewAppLaunched\">\n"
" <arg type=\"s\" name=\"appID\"/>\n"
" </signal>\n"
" <signal name=\"UninstallSuccess\">\n"
" <arg type=\"s\" name=\"appID\"/>\n"
" </signal>\n"
" <signal name=\"UninstallFailed\">\n"
" <arg type=\"s\" name=\"appId\"/>\n"
" <arg type=\"s\" name=\"errMsg\"/>\n"
" </signal>\n"
" <property access=\"readwrite\" type=\"b\" name=\"Fullscreen\"/>\n"
" <property access=\"readwrite\" type=\"i\" name=\"DisplayMode\"/>\n"
" </interface>\n"
"")
public:
DBusAdaptorLauncher(QObject *parent);
virtual ~DBusAdaptorLauncher();
Launcher *parent() const;
public: // PROPERTIES
Q_PROPERTY(int DisplayMode READ displayMode WRITE setDisplayMode NOTIFY DisplayModeChanged)
int displayMode() const;
void setDisplayMode(int value);
Q_PROPERTY(bool Fullscreen READ fullscreen WRITE setFullscreen NOTIFY FullScreenChanged)
bool fullscreen() const;
void setFullscreen(bool value);
public Q_SLOTS: // METHODS
bool AddAutostart(const QString &desktopFile);
QString GetAllItemInfos();
QStringList GetAllNewInstalledApps();
bool GetDisableScaling(const QString &id);
QString GetItemInfo(const QString &id);
bool GetUseProxy(const QString &id);
bool IsItemOnDesktop(const QString &id);
bool LaunchWithTimestamp(const QString &desktopFile, int time);
bool RemoveAutostart(const QString &desktopFile);
bool RequestRemoveFromDesktop(const QString &id);
bool RequestSendToDesktop(const QString &id);
void RequestUninstall(const QString &id);
void SetDisableScaling(const QString &id, bool value);
void SetUseProxy(const QString &id, bool value);
Q_SIGNALS: // SIGNALS
void ItemChanged(const QString &status, const QString &itemInfo, qlonglong categoryID);
void NewAppLaunched(const QString &appID);
void UninstallFailed(const QString &appId, const QString &errMsg);
void UninstallSuccess(const QString &appID);
void DisplayModeChanged(int mode);
void FullScreenChanged(bool isFull);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,158 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LAUNCHER_H
#define LAUNCHER_H
#include "common.h"
#include "synmodule.h"
#include "category.h"
#include <QObject>
#include <QMap>
#include <QVector>
#include <QDBusMessage>
#include <QDBusContext>
// 同步数据
struct SyncData {
QString version;
QString display_mode;
bool fullscreen;
};
// 应用类型
enum class AppType {
Flatpak,
ChromeShortcut,
CrossOver,
WineApp,
Default
};
// 应用基本信息
struct ItemInfo
{
QString path;
QString name;
QString enName;
QString id;
QString icon;
Categorytype categoryId;
int64_t timeInstalled;
};
// 应用信息类
struct Item {
inline bool isValid() {return !info.path.isEmpty();}
ItemInfo info;
QStringList keywords;
QStringList categories;
QString xDeepinCategory;
QString exec;
QString genericName;
QString comment;
std::map<QString, int> searchTargets;
};
class DesktopInfo;
class Launcher : public SynModule, public QDBusContext
{
Q_OBJECT
public:
explicit Launcher(QObject *parent);
~Launcher();
// 设置配置
void setSynConfig(QByteArray ba);
QByteArray getSyncConfig();
const QMap<QString, Item> *getItems();
int getDisplayMode();
bool getFullScreen();
void setDisplayMode(int value);
void setFullScreen(bool isFull);
QVector<ItemInfo> getAllItemInfos();
QStringList getAllNewInstalledApps();
bool getDisableScaling(QString appId);
ItemInfo getItemInfo(QString appId);
bool getUseProxy(QString appId);
bool isItemOnDesktop(QString appId);
bool requestRemoveFromDesktop(QString appId);
bool requestSendToDesktop(QString appId);
void requestUninstall(QString appId);
void setDisableScaling(QString appId, bool value);
void setUseProxy(QString appId, bool value);
Q_SIGNALS:
void ItemChanged(QString status, ItemInfo itemInfo, Categorytype ty);
void NewAppLaunched(QString appId);
void UninstallSuccess(QString appId);
void UninstallFailed(QString appId, QString errMsg);
void displayModeChanged(int mode);
void fullScreenChanged(bool isFull);
private Q_SLOTS:
void handleFSWatcherEvents(QDBusMessage msg);
void handleLRecoderRestart(QDBusMessage msg);
private:
void initSettings();
void loadDesktopPkgMap();
void loadPkgCategoryMap();
void checkDesktopFile(QString filePath);
void handleAppHiddenChanged();
void loadNameMap();
void initItems();
QString getAppIdByFilePath(QString filePath, QStringList dirs);
bool isDeepinCustomDesktopFile(QString fileName);
Item NewItemWithDesktopInfo(DesktopInfo &info);
void addItem(Item item);
Categorytype queryCategoryId(const Item *item);
Categorytype getXCategory(const Item *item);
QString queryPkgName(const QString &itemID, const QString &itemPath);
QString queryPkgNameWithDpkg(const QString &itemPath);
Item getItemByPath(QString itemPath);
void watchDataDirs();
void emitItemChanged(const Item *item, QString status);
AppType getAppType(DesktopInfo &info, const Item &item);
bool doUninstall(DesktopInfo &info, const Item &item);
bool uninstallFlatpak(DesktopInfo &info, const Item &item);
bool uninstallWineApp(const Item &item);
bool uninstallSysApp(const QString &name, const QString &pkg);
bool removeDesktop(const Item &item);
void notifyUninstallDone(const Item &item, bool result);
QMap<QString, Item> itemsMap;
QMap<QString, QString> desktopPkgMap;
QMap<QString, Categorytype> pkgCategoryMap;
QMap<QString, QString> nameMap;
QMap<QString, int> noPkgItemIds;
QVector<QString> appsHidden;
QStringList appDirs;
};
#endif // LAUNCHER_H

View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "launchermanager.h"
#include "launcher.h"
#include "dbusadaptorlauncher.h"
LauncherManager::LauncherManager(QObject *parent)
: QObject(parent)
, launcher(new Launcher(this))
{
new DBusAdaptorLauncher(launcher);
QDBusConnection con = QDBusConnection::sessionBus();
if (!con.registerService(dbusService))
{
qInfo() << "register service Launcher1 error:" << con.lastError().message();
}
if (!con.registerObject(dbusPath, launcher))
{
qInfo() << "register object Launcher1 error:" << con.lastError().message();
}
}
LauncherManager::~LauncherManager()
{
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LAUNCHERMANAGER_H
#define LAUNCHERMANAGER_H
#include <QObject>
class Launcher;
class LauncherManager : public QObject
{
Q_OBJECT
public:
explicit LauncherManager(QObject *parent = nullptr);
~LauncherManager();
private:
Launcher *launcher;
};
#endif // LAUNCHERMANAGER_H

View File

@ -0,0 +1,128 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "launchersettings.h"
#include "settings.h"
#include <DConfig>
#include <QDebug>
#include <QJsonObject>
#include <QJsonDocument>
DCORE_USE_NAMESPACE
static DConfig *dconfig = Settings::ConfigPtr(configLauncher);
QString LauncherSettings::getDisplayMode()
{
return dconfig ? dconfig->value(keyDisplayMode).toString() : "";
}
void LauncherSettings::setDisplayMode(QString value)
{
if (dconfig) {
dconfig->setValue(keyDisplayMode, value);
}
}
int LauncherSettings::getFullscreenMode()
{
return dconfig ? dconfig->value(keyFullscreen).toBool() : false;
}
void LauncherSettings::setFullscreenMode(int value)
{
if (dconfig) {
dconfig->setValue(keyFullscreen, value);
}
}
QVector<QString> LauncherSettings::getDisableScalingApps()
{
QVector<QString> ret;
if (dconfig) {
QList<QVariant> apps = dconfig->value(keyAppsDisableScaling).toList();
for (auto app : apps) {
ret.push_back(app.toString());
}
}
return ret;
}
void LauncherSettings::setDisableScalingApps(const QVector<QString> &value)
{
if (dconfig) {
QList<QVariant> apps;
for (const auto &app : value)
apps.push_back(app);
dconfig->setValue(keyAppsDisableScaling, apps);
}
}
QVector<QString> LauncherSettings::getUseProxyApps()
{
QVector<QString> ret;
if (dconfig) {
QList<QVariant> apps = dconfig->value(keyAppsUseProxy).toList();
for (auto app : apps) {
ret.push_back(app.toString());
}
}
return ret;
}
void LauncherSettings::setUseProxy(const QVector<QString> &value)
{
if (dconfig) {
QList<QVariant> apps;
for (const auto &app : value)
apps.push_back(app);
dconfig->setValue(keyAppsUseProxy, apps);
}
}
QVector<QString> LauncherSettings::getHiddenApps()
{
QVector<QString> ret;
if (dconfig) {
QList<QVariant> hiddenApps = dconfig->value(keyAppsHidden).toList();
for (auto app : hiddenApps) {
ret.push_back(app.toString());
}
}
return ret;
}
LauncherSettings::LauncherSettings(QObject *parent) : QObject(parent)
{
// 绑定属性
connect(dconfig, &DConfig::valueChanged, this, [&] (const QString &key) {
if (key == keyDisplayMode) {
Q_EMIT displayModeChanged(dconfig->value(key).toString());
} else if (key == keyFullscreen) {
Q_EMIT fullscreenChanged(dconfig->value(key).toBool());
} else if (key == keyAppsHidden) {
Q_EMIT hiddenAppsChanged();
}
});
}

View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
*
* Author: weizhixiang <weizhixiang@uniontech.com>
*
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LAUNCHERSETTINGS_H
#define LAUNCHERSETTINGS_H
#include "common.h"
#include <QObject>
#include <QVector>
class LauncherSettings : public QObject
{
Q_OBJECT
public:
static inline LauncherSettings *instance() {
static LauncherSettings instance;
return &instance;
}
QString getDisplayMode();
void setDisplayMode(QString value);
int getFullscreenMode();
void setFullscreenMode(int value);
QVector<QString> getDisableScalingApps();
void setDisableScalingApps(const QVector<QString> &value);
QVector<QString> getUseProxyApps();
void setUseProxy(const QVector<QString> &value);
QVector<QString> getHiddenApps();
Q_SIGNALS:
void displayModeChanged(QString mode);
void fullscreenChanged(bool isFull);
void hiddenAppsChanged();
private:
LauncherSettings(QObject *paret = nullptr);
LauncherSettings(const LauncherSettings &);
LauncherSettings& operator= (const LauncherSettings &);
};
#endif // LAUNCHERSETTINGS_H