2022-04-24 14:52:13 +08:00
|
|
|
|
/*
|
2022-05-15 12:10:42 +08:00
|
|
|
|
* Copyright (C) 2021 ~ 2022 Deepin Technology Co., Ltd.
|
2022-04-24 14:52:13 +08:00
|
|
|
|
*
|
|
|
|
|
* 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>
|
2022-05-24 21:49:47 +08:00
|
|
|
|
#include <QTimer>
|
2022-05-31 17:20:39 +08:00
|
|
|
|
#include <QImage>
|
|
|
|
|
#include <QIcon>
|
2022-04-24 14:52:13 +08:00
|
|
|
|
|
|
|
|
|
#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")
|
2022-05-24 21:49:47 +08:00
|
|
|
|
|| atom == XCB->getAtom("_NET_WM_WINDOW_TYPE_DESKTOP") // 桌面属性窗口
|
2022-04-24 14:52:13 +08:00
|
|
|
|
|| atom == XCB->getAtom("_NET_WM_WINDOW_TYPE_DND")
|
2022-05-24 21:49:47 +08:00
|
|
|
|
|| atom == XCB->getAtom("_NET_WM_WINDOW_TYPE_DOCK") // 任务栏属性窗口
|
2022-04-24 14:52:13 +08:00
|
|
|
|
|| 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()
|
|
|
|
|
{
|
2022-05-24 21:49:47 +08:00
|
|
|
|
XCB->changeActiveWindow(xid);
|
2022-05-31 21:36:06 +08:00
|
|
|
|
QTimer::singleShot(50, [&] {
|
2022-05-24 21:49:47 +08:00
|
|
|
|
XCB->restackWindow(xid);
|
|
|
|
|
});
|
2022-04-24 14:52:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-26 18:10:53 +08:00
|
|
|
|
/**
|
|
|
|
|
* @brief WindowInfoX::genInnerId 生成innerId
|
|
|
|
|
* @param winInfo
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
2022-04-24 14:52:13 +08:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-24 21:49:47 +08:00
|
|
|
|
/**
|
2022-05-27 21:03:41 +08:00
|
|
|
|
* @brief WindowInfoX::update 更新窗口信息(在识别窗口时执行一次)
|
2022-05-24 21:49:47 +08:00
|
|
|
|
*/
|
2022-04-24 14:52:13 +08:00
|
|
|
|
void WindowInfoX::update()
|
|
|
|
|
{
|
|
|
|
|
updateWmClass();
|
|
|
|
|
updateWmState();
|
|
|
|
|
updateWmWindowType();
|
|
|
|
|
updateWmAllowedActions();
|
|
|
|
|
updateHasWmTransientFor();
|
|
|
|
|
updateProcessInfo();
|
|
|
|
|
updateWmName();
|
|
|
|
|
innerId = genInnerId(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO 从窗口中获取图标, 并设置best size be used in Entry
|
|
|
|
|
QString WindowInfoX::getIconFromWindow()
|
|
|
|
|
{
|
2022-05-31 17:20:39 +08:00
|
|
|
|
return QString();
|
2022-04-24 14:52:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2022-06-27 10:15:19 +08:00
|
|
|
|
qInfo() << "updateProcessInfo: pid=" << pid;
|
2022-04-24 14:52:13 +08:00
|
|
|
|
processInfo = new ProcessInfo(pid);
|
2022-06-27 10:15:19 +08:00
|
|
|
|
if (!processInfo->isValid()) {
|
2022-04-24 14:52:13 +08:00
|
|
|
|
// try WM_COMMAND
|
|
|
|
|
auto wmComand = XCB->getWMCommand(winId);
|
2022-06-27 10:15:19 +08:00
|
|
|
|
if (wmComand.size() > 0) {
|
|
|
|
|
delete processInfo;
|
2022-04-24 14:52:13 +08:00
|
|
|
|
processInfo = new ProcessInfo(wmComand);
|
2022-06-27 10:15:19 +08:00
|
|
|
|
}
|
2022-04-24 14:52:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|