类成员变量前统一增加m_标记 log: 代码格式化 Influence: 无 Task: https://pms.uniontech.com/task-view-96831.html Change-Id: I2432dd5667bc195da1a64cdbb7cd933052ba7baa
468 lines
11 KiB
C++
468 lines
11 KiB
C++
/*
|
||
* Copyright (C) 2021 ~ 2022 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>
|
||
#include <QTimer>
|
||
#include <QImage>
|
||
#include <QIcon>
|
||
|
||
#define XCB XCBUtils::instance()
|
||
|
||
WindowInfoX::WindowInfoX(XWindow _xid)
|
||
: WindowInfoBase ()
|
||
, m_x(0)
|
||
, m_y(0)
|
||
, m_width(0)
|
||
, m_height(0)
|
||
, m_hasWMTransientFor(false)
|
||
, m_hasXEmbedInfo(false)
|
||
, m_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 (!m_updateCalled) {
|
||
update();
|
||
m_updateCalled = true;
|
||
}
|
||
|
||
if (hasWmStateSkipTaskBar() || isValidModal() || shouldSkipWithWMClass())
|
||
return true;
|
||
|
||
for (auto atom : m_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->changeActiveWindow(xid);
|
||
QTimer::singleShot(50, [&] {
|
||
XCB->restackWindow(xid);
|
||
});
|
||
}
|
||
|
||
void WindowInfoX::minimize()
|
||
{
|
||
XCB->minimizeWindow(xid);
|
||
}
|
||
|
||
bool WindowInfoX::isMinimized()
|
||
{
|
||
return containAtom(m_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 ((m_motifWmHints.flags & MotifHintFunctions) == 0
|
||
|| (m_motifWmHints.functions & MotifFunctionAll) != 0
|
||
|| (m_motifWmHints.functions & MotifFunctionClose) != 0)
|
||
return true;
|
||
|
||
for (auto action : m_wmAllowedActions) {
|
||
if (action == XCB->getAtom("_NET_WM_ACTION_CLOSE")) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
QString WindowInfoX::getDisplayName()
|
||
{
|
||
XWindow winId = xid;
|
||
//QString role = wmRole;
|
||
QString className(m_wmClass.className.c_str());
|
||
QString instance;
|
||
if (m_wmClass.instanceName.size() > 0) {
|
||
int pos = QString(m_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 = m_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::uuid()
|
||
{
|
||
return QString();
|
||
}
|
||
|
||
QString WindowInfoX::getGtkAppId()
|
||
{
|
||
return m_gtkAppId;
|
||
}
|
||
|
||
QString WindowInfoX::getFlatpakAppId()
|
||
{
|
||
return m_flatpakAppId;
|
||
}
|
||
|
||
QString WindowInfoX::getWmRole()
|
||
{
|
||
return m_wmRole;
|
||
}
|
||
|
||
WMClass WindowInfoX::getWMClass()
|
||
{
|
||
return m_wmClass;
|
||
}
|
||
|
||
QString WindowInfoX::getWMName()
|
||
{
|
||
return m_wmName;
|
||
}
|
||
|
||
ConfigureEvent *WindowInfoX::getLastConfigureEvent()
|
||
{
|
||
return m_lastConfigureNotifyEvent;
|
||
}
|
||
|
||
void WindowInfoX::setLastConfigureEvent(ConfigureEvent *event)
|
||
{
|
||
m_lastConfigureNotifyEvent = event;
|
||
}
|
||
|
||
bool WindowInfoX::isGeometryChanged(int _x, int _y, int _width, int _height)
|
||
{
|
||
return !(_x == m_x && _y == m_y && _width == m_width && _height == m_height);
|
||
}
|
||
|
||
void WindowInfoX::setGtkAppId(QString _gtkAppId)
|
||
{
|
||
m_gtkAppId = _gtkAppId;
|
||
}
|
||
|
||
void WindowInfoX::updateMotifWmHints()
|
||
{
|
||
// get from XCB
|
||
m_motifWmHints = XCB->getWindowMotifWMHints(xid);
|
||
}
|
||
|
||
// XEmbed info
|
||
// 一般 tray icon 会带有 _XEMBED_INFO 属性
|
||
void WindowInfoX::updateHasXEmbedInfo()
|
||
{
|
||
m_hasXEmbedInfo = XCB->hasXEmbedInfo(xid);
|
||
}
|
||
|
||
/**
|
||
* @brief WindowInfoX::genInnerId 生成innerId
|
||
* @param winInfo
|
||
* @return
|
||
*/
|
||
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()
|
||
{
|
||
m_wmWindowType.clear();
|
||
for (auto ty : XCB->getWMWindoType(xid)) {
|
||
m_wmWindowType.push_back(ty);
|
||
}
|
||
}
|
||
|
||
// 更新窗口许可动作
|
||
void WindowInfoX::updateWmAllowedActions()
|
||
{
|
||
m_wmAllowedActions.clear();
|
||
for (auto action : XCB->getWMAllowedActions(xid)) {
|
||
m_wmAllowedActions.push_back(action);
|
||
}
|
||
}
|
||
|
||
void WindowInfoX::updateWmState()
|
||
{
|
||
m_wmState.clear();
|
||
for (auto a : XCB->getWMState(xid)) {
|
||
m_wmState.push_back(a);
|
||
}
|
||
}
|
||
|
||
void WindowInfoX::updateWmClass()
|
||
{
|
||
m_wmClass = XCB->getWMClass(xid);
|
||
}
|
||
|
||
void WindowInfoX::updateWmName()
|
||
{
|
||
auto name = XCB->getWMName(xid);
|
||
if (!name.empty())
|
||
m_wmName = name.c_str();
|
||
|
||
title = getTitle();
|
||
}
|
||
|
||
void WindowInfoX::updateIcon()
|
||
{
|
||
icon = getIconFromWindow();
|
||
}
|
||
|
||
void WindowInfoX::updateHasWmTransientFor()
|
||
{
|
||
if (XCB->getWMTransientFor(xid) == 1)
|
||
m_hasWMTransientFor = true;
|
||
}
|
||
|
||
/**
|
||
* @brief WindowInfoX::update 更新窗口信息(在识别窗口时执行一次)
|
||
*/
|
||
void WindowInfoX::update()
|
||
{
|
||
updateWmClass();
|
||
updateWmState();
|
||
updateWmWindowType();
|
||
updateWmAllowedActions();
|
||
updateHasWmTransientFor();
|
||
updateProcessInfo();
|
||
updateWmName();
|
||
innerId = genInnerId(this);
|
||
}
|
||
|
||
// TODO 从窗口中获取图标, 并设置best size be used in Entry
|
||
QString WindowInfoX::getIconFromWindow()
|
||
{
|
||
return QString();
|
||
}
|
||
|
||
bool WindowInfoX::isActionMinimizeAllowed()
|
||
{
|
||
return containAtom(m_wmAllowedActions, XCB->getAtom("_NET_WM_ACTION_MINIMIZE"));
|
||
}
|
||
|
||
bool WindowInfoX::hasWmStateDemandsAttention()
|
||
{
|
||
return containAtom(m_wmState, XCB->getAtom("_NET_WM_STATE_DEMANDS_ATTENTION"));
|
||
}
|
||
|
||
bool WindowInfoX::hasWmStateSkipTaskBar()
|
||
{
|
||
return containAtom(m_wmState, XCB->getAtom("_NET_WM_STATE_SKIP_TASKBAR"));
|
||
}
|
||
|
||
bool WindowInfoX::hasWmStateModal()
|
||
{
|
||
return containAtom(m_wmState, XCB->getAtom("_NET_WM_STATE_MODAL"));
|
||
}
|
||
|
||
bool WindowInfoX::isValidModal()
|
||
{
|
||
return hasWmStateModal() && hasWmStateModal();
|
||
}
|
||
|
||
// 通过WMClass判断是否需要隐藏此窗口
|
||
bool WindowInfoX::shouldSkipWithWMClass()
|
||
{
|
||
bool ret = false;
|
||
if (m_wmClass.instanceName == "explorer.exe" && m_wmClass.className == "Wine")
|
||
ret = true;
|
||
else if (m_wmClass.className == "dde-launcher")
|
||
ret = true;
|
||
|
||
return ret;
|
||
}
|
||
|
||
void WindowInfoX::updateProcessInfo()
|
||
{
|
||
XWindow winId = xid;
|
||
pid = XCB->getWMPid(winId);
|
||
if (processInfo)
|
||
delete processInfo;
|
||
|
||
qInfo() << "updateProcessInfo: pid=" << pid;
|
||
processInfo = new ProcessInfo(pid);
|
||
if (!processInfo->isValid()) {
|
||
// try WM_COMMAND
|
||
auto wmComand = XCB->getWMCommand(winId);
|
||
if (wmComand.size() > 0) {
|
||
delete processInfo;
|
||
processInfo = new ProcessInfo(wmComand);
|
||
}
|
||
}
|
||
|
||
qInfo() << "updateProcessInfo: pid is " << pid;
|
||
}
|
||
|
||
bool WindowInfoX::getUpdateCalled()
|
||
{
|
||
return m_updateCalled;
|
||
}
|
||
|
||
void WindowInfoX::setInnerId(QString _innerId)
|
||
{
|
||
innerId = _innerId;
|
||
}
|
||
|
||
QString WindowInfoX::getTitle()
|
||
{
|
||
QString name = m_wmName;
|
||
if (name.isEmpty())
|
||
name = getDisplayName();
|
||
|
||
return name;
|
||
}
|
||
|
||
bool WindowInfoX::isDemandingAttention()
|
||
{
|
||
return hasWmStateDemandsAttention();
|
||
}
|
||
|
||
void WindowInfoX::close(uint32_t timestamp)
|
||
{
|
||
XCB->requestCloseWindow(xid, timestamp);
|
||
}
|
||
|
||
|