dde-dock/frame/taskmanager/windowidentify.cpp
tsic404 9de057e2cc refactor: add taskmanager from dde-application-manager
1. taskmanager used to identify which entry should map to window
  in x11 environmrnt, listen to xevent in anohter thread, and handle those event
when window create, destory, changed. use some way to identify which entry(desktopfile)
should mapped to changed window.
  in wayland, connected plsamawindow signal(window created destoried.
2. use taskmanager instead of dbus in old dock code

log: as title
2023-07-18 07:35:19 +00:00

532 lines
20 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-FileCopyrightText: 2018 - 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include "windowidentify.h"
#include "common.h"
#include "appinfo.h"
#include "taskmanager.h"
#include "processinfo.h"
#include "taskmanager/desktopinfo.h"
#include "xcbutils.h"
#include "bamfdesktop.h"
#include <QDebug>
#include <QThread>
#include <qstandardpaths.h>
#define XCB XCBUtils::instance()
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(TaskManager *_taskmanager, QObject *parent)
: QObject(parent)
, m_taskmanager(_taskmanager)
{
m_identifyWindowFuns << qMakePair(QString("Android") , &identifyWindowAndroid);
m_identifyWindowFuns << qMakePair(QString("PidEnv"), &identifyWindowByPidEnv);
m_identifyWindowFuns << qMakePair(QString("CmdlineTurboBooster"), &identifyWindowByCmdlineTurboBooster);
m_identifyWindowFuns << qMakePair(QString("Cmdline-XWalk"), &identifyWindowByCmdlineXWalk);
m_identifyWindowFuns << qMakePair(QString("FlatpakAppID"), &identifyWindowByFlatpakAppID);
m_identifyWindowFuns << qMakePair(QString("CrxId"), &identifyWindowByCrxId);
m_identifyWindowFuns << qMakePair(QString("Rule"), &identifyWindowByRule);
m_identifyWindowFuns << qMakePair(QString("Bamf"), &identifyWindowByBamf);
m_identifyWindowFuns << qMakePair(QString("Pid"), &identifyWindowByPid);
m_identifyWindowFuns << qMakePair(QString("Scratch"), &identifyWindowByScratch);
m_identifyWindowFuns << qMakePair(QString("GtkAppId"), &identifyWindowByGtkAppId);
m_identifyWindowFuns << qMakePair(QString("WmClass"), &identifyWindowByWmClass);
}
AppInfo *WindowIdentify::identifyWindow(WindowInfoBase *winInfo, QString &innerId)
{
if (!winInfo)
return nullptr;
qInfo() << "identifyWindow: window id " << winInfo->getXid() << " innerId " << winInfo->getInnerId();
if (winInfo->getWindowType() == "X11")
return identifyWindowX11(static_cast<WindowInfoX *>(winInfo), innerId);
if (winInfo->getWindowType() == "Wayland")
return identifyWindowWayland(static_cast<WindowInfoK *>(winInfo), innerId);
return nullptr;
}
AppInfo *WindowIdentify::identifyWindowX11(WindowInfoX *winInfo, QString &innerId)
{
AppInfo *appInfo = nullptr;
if (winInfo->getInnerId().isEmpty()) {
qInfo() << "identifyWindowX11: window innerId is empty";
return appInfo;
}
for (auto iter = m_identifyWindowFuns.begin(); iter != m_identifyWindowFuns.end(); iter++) {
QString name = iter->first;
IdentifyFunc func = iter->second;
qInfo() << "identifyWindowX11: try " << name;
appInfo = func(m_taskmanager, 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";
// 如果识别窗口失败则该app的entryInnerId使用当前窗口的innerId
innerId = winInfo->getInnerId();
return appInfo;
}
AppInfo *WindowIdentify::identifyWindowWayland(WindowInfoK *winInfo, QString &innerId)
{
// TODO: 对桌面调起的文管应用做规避处理需要在此处添加因为初始化时appId和title为空
if (winInfo->getAppId() == "dde-desktop" && m_taskmanager->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();
QString desktopFilePath = process->getEnv("GIO_LAUNCHED_DESKTOP_FILE");
if ((desktopFilePath.endsWith(".desktop"))) {
appInfo = new AppInfo(desktopFilePath);
}
}
// autoStart
if (appInfo->isValidApp()) {
AppInfo *fixedAppInfo = fixAutostartAppInfo(appInfo->getFileName());
if (fixedAppInfo) {
delete appInfo;
appInfo = fixedAppInfo;
appInfo->setIdentifyMethod("FixAutostart");
}
}
if (appInfo)
innerId = appInfo->getInnerId();
return appInfo;
}
AppInfo *WindowIdentify::identifyWindowAndroid(TaskManager *_taskmanager, 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);
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(TaskManager *_taskmanager, 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) {
return ret;
}
QString launchedDesktopFile = process->getEnv("GIO_LAUNCHED_DESKTOP_FILE");
QString launchedDesktopFilePidStr = process->getEnv("GIO_LAUNCHED_DESKTOP_FILE_PID");
int launchedDesktopFilePid = launchedDesktopFilePidStr.toInt();
qInfo() << "launchedDesktopFilePid=" << launchedDesktopFilePid << " launchedDesktopFile=" << launchedDesktopFile;
if (launchedDesktopFile.isEmpty()) {
return ret;
}
auto pidIsSh = [](int pid) -> bool {
ProcessInfo parentProcess(pid);
auto parentCmdLine = parentProcess.getCmdLine();
if (parentCmdLine.size() <= 0) {
return false;
}
qInfo() << "ppid equal" << "parentCmdLine[0]:" << parentCmdLine[0];
QString cmd0 = parentCmdLine[0];
int pos = cmd0.lastIndexOf('/');
if (pos > 0)
cmd0 = cmd0.remove(0, pos + 1);
if (cmd0 == "sh" || cmd0 == "bash"){
return true;
}
return false;
};
auto processInLinglong = [](ProcessInfo* const process) -> bool {
for (ProcessInfo p(process->getPpid()); p.getPid() != 0; p = ProcessInfo(p.getPpid())) {
if (!p.getCmdLine().size()){
qWarning() << "Failed to get command line of" << p.getPid() << " SKIP it.";
continue;
}
if (p.getCmdLine()[0].indexOf("ll-box") != -1) {
qDebug() << "process ID" << process->getPid() << "is in linglong container,"
<<"ll-box PID" << p.getPid();
return true;
}
}
return false;
};
// 以下几种情况下,才能信任环境变量 GIO_LAUNCHED_DESKTOP_FILE。
if (pid == launchedDesktopFilePid || // 当窗口pid和launchedDesktopFilePid相同时
( process->getPpid() &&
process->getPpid() == launchedDesktopFilePid &&
pidIsSh(process->getPpid())
) || // 当窗口的进程的父进程id即ppid和launchedDesktopFilePid相同并且该父进程是sh或bash时。
processInLinglong(process) // 当窗口pid在玲珑容器中
) {
ret = new AppInfo(launchedDesktopFile);
innerId = ret->getInnerId();
}
return ret;
}
AppInfo *WindowIdentify::identifyWindowByCmdlineTurboBooster(TaskManager *_taskmanager, 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 (cmdline[0].startsWith(".desktop")) {
desktopFile = cmdline[0];
} else if (QString(cmdline[0]).contains("/applications/")) {
QFileInfo fileInfo(cmdline[0]);
QString path = fileInfo.path();
QString base = fileInfo.completeBaseName();
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(TaskManager *_taskmanager, 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();
QFileInfo file(exe);
QString exeBase = file.completeBaseName();
auto args = process->getArgs();
if (exe != "xwalk" || args.size() == 0)
break;
QString lastArg = args[args.size() - 1];
file.setFile(lastArg);
if (file.completeBaseName() == "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(TaskManager *_taskmanager, 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(TaskManager *_taskmanager, 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(TaskManager *_taskmanager, WindowInfoX *winInfo, QString &innerId)
{
static WindowPatterns patterns;
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");
if (!launchedDesktopFile.isEmpty())
ret = new AppInfo(launchedDesktopFile);
}
} else {
qInfo() << "patterns match bad result";
}
if (ret)
innerId = ret->getInnerId();
return ret;
}
AppInfo *WindowIdentify::identifyWindowByBamf(TaskManager *_taskmanager, WindowInfoX *winInfo, QString &innerId)
{
if (_taskmanager->isWaylandEnv()) {
return nullptr;
}
AppInfo *ret = nullptr;
XWindow xid = winInfo->getXid();
qInfo() << "identifyWindowByBamf: windowId=" << xid;
QString desktopFile;
// 重试 bamf 识别,部分的窗口经常要多次调用才能识别到。
for (int i = 0; i < 3; i++) {
desktopFile = _taskmanager->getDesktopFromWindowByBamf(xid);
if (!desktopFile.isEmpty())
break;
}
if (!desktopFile.isEmpty()) {
ret = new AppInfo(desktopFile);
innerId = ret->getInnerId();
}
return ret;
}
AppInfo *WindowIdentify::identifyWindowByPid(TaskManager *_taskmanager, WindowInfoX *winInfo, QString &innerId)
{
AppInfo *ret = nullptr;
if (winInfo->getPid() > 10) {
auto entry = _taskmanager->getEntryByWindowId(winInfo->getPid());
if (entry) {
ret = entry->getAppInfo();
innerId = ret->getInnerId();
}
}
return ret;
}
AppInfo *WindowIdentify::identifyWindowByScratch(TaskManager *_taskmanager, 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(TaskManager *_taskmanager, 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(TaskManager *_taskmanager, WindowInfoX *winInfo, QString &innerId)
{
WMClass wmClass = winInfo->getWMClass();
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 (DesktopInfo("org.deepin.flatdeb." + QString(wmClass.instanceName.c_str()).toLower()).isValidDesktop()) {
AppInfo *appInfo = new AppInfo("org.deepin.flatdeb." + QString(wmClass.instanceName.c_str()).toLower());
innerId = appInfo->getInnerId();
return appInfo;
}
if (DesktopInfo(QString::fromStdString(wmClass.instanceName)).isValidDesktop()) {
AppInfo *appInfo = new AppInfo(wmClass.instanceName.c_str());
innerId = appInfo->getInnerId();
return appInfo;
}
}
if (wmClass.className.size() > 0) {
QString filename = QString::fromStdString(wmClass.className);
bool isValid = DesktopInfo(filename).isValidDesktop();
if (!isValid) {
filename = BamfDesktop::instance()->fileName(wmClass.instanceName.c_str());
isValid = DesktopInfo(filename).isValidDesktop();
}
if (isValid) {
AppInfo *appInfo = new AppInfo(filename);
innerId = appInfo->getInnerId();
return appInfo;
}
}
return nullptr;
}
AppInfo *WindowIdentify::fixAutostartAppInfo(QString fileName)
{
QFileInfo file(fileName);
QString filePath = file.absolutePath();
bool isAutoStart = false;
for (auto dir : QStandardPaths::standardLocations(QStandardPaths::ConfigLocation)) {
if (dir.contains(filePath)) {
isAutoStart = true;
break;
}
}
return isAutoStart ? new AppInfo(file.completeBaseName()) : nullptr;
}
int32_t WindowIdentify::getAndroidUengineId(XWindow winId)
{
// TODO 获取AndroidUengineId
return 0;
}
QString WindowIdentify::getAndroidUengineName(XWindow winId)
{
// TODO 获取AndroidUengineName
return "";
}