/* * Copyright (C) 2021 ~ 2022 Deepin Technology Co., Ltd. * * Author: weizhixiang * * Maintainer: weizhixiang * * 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 . */ #include "entry.h" #include "dock.h" #include "processinfo.h" #include "dbusadaptorentry.h" #include #include #include 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 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> 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 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 Entry::getAllowedClosedWindowIds() { QVector 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 Entry::getExportWindowInfos() { return exportWindowInfos; } QVector Entry::getAllowedCloseWindows() { QVector 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 Entry::getMenuItemDesktopActions() { QVector 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; }