fix: 修复重启AM后浏览器没有显示在任务栏的问题
修复重启AM后浏览器没有显示在任务栏的问题 Log: Task: https://pms.uniontech.com/task-view-137667.html Influence: 无 Change-Id: I147ff7d2329f0d568496fdb6970925392e485d77
This commit is contained in:
parent
e817c291bc
commit
42d50bae4f
@ -271,7 +271,7 @@ std::string DesktopInfo::getId()
|
|||||||
idStr = m_fileName.substr(0, m_fileName.size() - 8); // trim suffix
|
idStr = m_fileName.substr(0, m_fileName.size() - 8); // trim suffix
|
||||||
size_t dirPos = idStr.find("/applications/");
|
size_t dirPos = idStr.find("/applications/");
|
||||||
if (dirPos == std::string::npos)
|
if (dirPos == std::string::npos)
|
||||||
return "";
|
return idStr;
|
||||||
|
|
||||||
std::string baseDir(idStr.substr(0, dirPos + 14)); // length of "/applications/" is 14
|
std::string baseDir(idStr.substr(0, dirPos + 14)); // length of "/applications/" is 14
|
||||||
std::vector<std::string> appDirs = BaseDir::appDirs();
|
std::vector<std::string> appDirs = BaseDir::appDirs();
|
||||||
|
@ -145,3 +145,16 @@ void DString::delQuote(std::string &str)
|
|||||||
str.assign(str.substr(1, str.size() - 2));
|
str.assign(str.substr(1, str.size() - 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string DString::join(std::vector<std::string> strs, std::string joinStr)
|
||||||
|
{
|
||||||
|
std::string ret;
|
||||||
|
for (uint i = 0; i < strs.size(); i++) {
|
||||||
|
if (i < strs.size() - 1) {
|
||||||
|
ret += strs[i] + joinStr;
|
||||||
|
} else {
|
||||||
|
ret += strs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -51,6 +51,8 @@ public:
|
|||||||
// 去除首尾引用
|
// 去除首尾引用
|
||||||
static char *delQuote(const char *chars);
|
static char *delQuote(const char *chars);
|
||||||
static void delQuote(std::string &str);
|
static void delQuote(std::string &str);
|
||||||
|
// 连接字符串
|
||||||
|
static std::string join(std::vector<std::string> strs, std::string joinStr);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DSTRING_H
|
#endif // DSTRING_H
|
||||||
|
@ -441,18 +441,13 @@ WMClass XCBUtils::getWMClass(XWindow xid)
|
|||||||
xcb_icccm_get_wm_class_reply_t reply;
|
xcb_icccm_get_wm_class_reply_t reply;
|
||||||
reply.instance_name = nullptr;
|
reply.instance_name = nullptr;
|
||||||
reply.class_name = nullptr;
|
reply.class_name = nullptr;
|
||||||
if (!xcb_icccm_get_wm_class_reply(m_connect, cookie, &reply, nullptr)) {
|
xcb_icccm_get_wm_class_reply(m_connect, cookie, &reply, nullptr); // 返回值为0不一定表示失败, 故不做返回值判断
|
||||||
if (reply.class_name)
|
if (reply.class_name)
|
||||||
ret.className.assign(reply.class_name);
|
ret.className.assign(reply.class_name);
|
||||||
|
|
||||||
if (reply.instance_name)
|
if (reply.instance_name)
|
||||||
ret.instanceName.assign(reply.instance_name);
|
ret.instanceName.assign(reply.instance_name);
|
||||||
|
|
||||||
//xcb_icccm_get_wm_class_reply_wipe(&reply);
|
|
||||||
} else {
|
|
||||||
std::cout << xid << " getWMClass error" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,6 @@ DBusHandler::DBusHandler(Dock *_dock, QObject *parent)
|
|||||||
, wm(new com::deepin::WM("com.deepin.wm", "/com/deepin/wm", 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))
|
, wmSwitcher(new com::deepin::WMSwitcher("com.deepin.wmWMSwitcher", "/com/deepin/WMSwitcher", session, this))
|
||||||
, kwaylandManager(nullptr)
|
, kwaylandManager(nullptr)
|
||||||
, bamfMatcher(new org::ayatana::bamf::BamfMatcher("org.ayatana.bamf.matcher", "/org/ayatana/bamf/matcher", session, this))
|
|
||||||
{
|
{
|
||||||
// 关联org.deepin.dde.daemon.Launcher1事件 ItemChanged
|
// 关联org.deepin.dde.daemon.Launcher1事件 ItemChanged
|
||||||
connect(launcherEnd, &LauncherBackEnd::ItemChanged, this, &DBusHandler::handleLauncherItemChanged);
|
connect(launcherEnd, &LauncherBackEnd::ItemChanged, this, &DBusHandler::handleLauncherItemChanged);
|
||||||
@ -200,17 +199,22 @@ void DBusHandler::presentWindows(QList<uint> windows)
|
|||||||
wm->PresentWindows(windows);
|
wm->PresentWindows(windows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: 待优化点, 查看Bamf根据windowId获取对应应用desktopFile路径实现方式, 移除bamf依赖
|
||||||
QString DBusHandler::getDesktopFromWindowByBamf(XWindow windowId)
|
QString DBusHandler::getDesktopFromWindowByBamf(XWindow windowId)
|
||||||
{
|
{
|
||||||
QDBusPendingReply<QString> reply = bamfMatcher->ApplicationForXid(windowId);
|
QDBusInterface interface0 = QDBusInterface("org.ayatana.bamf", "/org/ayatana/bamf/matcher", "org.ayatana.bamf.matcher");
|
||||||
if (!reply.isValid())
|
QDBusReply<QString> replyApplication = interface0.call("ApplicationForXid", windowId);
|
||||||
|
QString appObjPath = replyApplication.value();
|
||||||
|
if (!replyApplication.isValid() || appObjPath.isEmpty())
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
QString appObjPath = reply.value();
|
|
||||||
|
|
||||||
org::ayatana::bamf::BamfApplication bamfApp("org.ayatana.bamf.application", appObjPath, session, this);
|
QDBusInterface interface = QDBusInterface("org.ayatana.bamf", appObjPath, "org.ayatana.bamf.application");
|
||||||
if (bamfApp.isValid())
|
QDBusReply<QString> replyDesktopFile = interface.call("DesktopFile");
|
||||||
return bamfApp.DesktopFile();
|
|
||||||
|
if (replyDesktopFile.isValid())
|
||||||
|
return replyDesktopFile.value();
|
||||||
|
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,6 @@
|
|||||||
#include "dbuskwaylandwindowmanager.h"
|
#include "dbuskwaylandwindowmanager.h"
|
||||||
#include "windowinfok.h"
|
#include "windowinfok.h"
|
||||||
#include "dbusplasmawindow.h"
|
#include "dbusplasmawindow.h"
|
||||||
#include "dbusbamfmatcher.h"
|
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QDBusConnection>
|
#include <QDBusConnection>
|
||||||
@ -86,7 +85,6 @@ private:
|
|||||||
com::deepin::WM *wm;
|
com::deepin::WM *wm;
|
||||||
com::deepin::WMSwitcher *wmSwitcher;
|
com::deepin::WMSwitcher *wmSwitcher;
|
||||||
com::deepin::daemon::kwayland::WindowManager *kwaylandManager;
|
com::deepin::daemon::kwayland::WindowManager *kwaylandManager;
|
||||||
org::ayatana::bamf::BamfMatcher *bamfMatcher;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DBUSHANDLER_H
|
#endif // DBUSHANDLER_H
|
||||||
|
@ -116,6 +116,11 @@ int ProcessInfo::getPpid()
|
|||||||
return process.getPpid();
|
return process.getPpid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ProcessInfo::initWithPid()
|
||||||
|
{
|
||||||
|
return hasPid;
|
||||||
|
}
|
||||||
|
|
||||||
std::string ProcessInfo::getExe()
|
std::string ProcessInfo::getExe()
|
||||||
{
|
{
|
||||||
return exe;
|
return exe;
|
||||||
|
@ -39,6 +39,7 @@ public:
|
|||||||
std::vector<std::string> getArgs();
|
std::vector<std::string> getArgs();
|
||||||
int getPid();
|
int getPid();
|
||||||
int getPpid();
|
int getPpid();
|
||||||
|
bool initWithPid();
|
||||||
std::string getExe();
|
std::string getExe();
|
||||||
std::string getOneCommandLine();
|
std::string getOneCommandLine();
|
||||||
std::string getShellScriptLines();
|
std::string getShellScriptLines();
|
||||||
|
@ -34,8 +34,6 @@
|
|||||||
|
|
||||||
#define XCB XCBUtils::instance()
|
#define XCB XCBUtils::instance()
|
||||||
|
|
||||||
WindowPatterns WindowIdentify::patterns;
|
|
||||||
|
|
||||||
static QMap<QString, QString> crxAppIdMap = {
|
static QMap<QString, QString> crxAppIdMap = {
|
||||||
{"crx_onfalgmmmaighfmjgegnamdjmhpjpgpi", "apps.com.aiqiyi"},
|
{"crx_onfalgmmmaighfmjgegnamdjmhpjpgpi", "apps.com.aiqiyi"},
|
||||||
{"crx_gfhkopakpiiaeocgofdpcpjpdiglpkjl", "apps.cn.kugou.hd"},
|
{"crx_gfhkopakpiiaeocgofdpcpjpdiglpkjl", "apps.cn.kugou.hd"},
|
||||||
@ -385,6 +383,7 @@ AppInfo *WindowIdentify::identifyWindowByCrxId(Dock *_dock, WindowInfoX *winInfo
|
|||||||
|
|
||||||
AppInfo *WindowIdentify::identifyWindowByRule(Dock *_dock, WindowInfoX *winInfo, QString &innerId)
|
AppInfo *WindowIdentify::identifyWindowByRule(Dock *_dock, WindowInfoX *winInfo, QString &innerId)
|
||||||
{
|
{
|
||||||
|
static WindowPatterns patterns;
|
||||||
qInfo() << "identifyWindowByRule: windowId=" << winInfo->getXid();
|
qInfo() << "identifyWindowByRule: windowId=" << winInfo->getXid();
|
||||||
AppInfo *ret = nullptr;
|
AppInfo *ret = nullptr;
|
||||||
QString matchStr = patterns.match(winInfo);
|
QString matchStr = patterns.match(winInfo);
|
||||||
@ -417,13 +416,11 @@ AppInfo *WindowIdentify::identifyWindowByBamf(Dock *_dock, WindowInfoX *winInfo,
|
|||||||
XWindow xid = winInfo->getXid();
|
XWindow xid = winInfo->getXid();
|
||||||
qInfo() << "identifyWindowByBamf: windowId=" << xid;
|
qInfo() << "identifyWindowByBamf: windowId=" << xid;
|
||||||
QString desktopFile;
|
QString desktopFile;
|
||||||
// 重试 bamf 识别,yozo office 的窗口经常要第二次时才能识别到。
|
// 重试 bamf 识别,部分的窗口经常要多次调用才能识别到。
|
||||||
for (int i=0; i<3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
desktopFile = _dock->getDesktopFromWindowByBamf(xid);
|
desktopFile = _dock->getDesktopFromWindowByBamf(xid);
|
||||||
if (!desktopFile.isEmpty())
|
if (!desktopFile.isEmpty())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
QThread::msleep(100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!desktopFile.isEmpty()) {
|
if (!desktopFile.isEmpty()) {
|
||||||
|
@ -67,7 +67,6 @@ private:
|
|||||||
static int32_t getAndroidUengineId(XWindow winId);
|
static int32_t getAndroidUengineId(XWindow winId);
|
||||||
static QString getAndroidUengineName(XWindow winId);
|
static QString getAndroidUengineName(XWindow winId);
|
||||||
|
|
||||||
static WindowPatterns patterns; // 根据rule识别
|
|
||||||
Dock *dock;
|
Dock *dock;
|
||||||
QMap<QString, IdentifyFunc> identifyWindowFuns;
|
QMap<QString, IdentifyFunc> identifyWindowFuns;
|
||||||
};
|
};
|
||||||
|
@ -350,7 +350,7 @@ void WindowInfoX::updateHasWmTransientFor()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief WindowInfoX::update 更新窗口信息(仅仅执行一次)
|
* @brief WindowInfoX::update 更新窗口信息(在识别窗口时执行一次)
|
||||||
*/
|
*/
|
||||||
void WindowInfoX::update()
|
void WindowInfoX::update()
|
||||||
{
|
{
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
|
|
||||||
#include "windowpatterns.h"
|
#include "windowpatterns.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "processinfo.h"
|
||||||
|
#include "dfile.h"
|
||||||
|
#include "dstring.h"
|
||||||
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
@ -56,53 +59,123 @@ bool equalIgnoreCase(QString key, QString value) {
|
|||||||
|
|
||||||
bool regexMatch(QString key, QString value) {
|
bool regexMatch(QString key, QString value) {
|
||||||
QRegExp ruleRegex(value);
|
QRegExp ruleRegex(value);
|
||||||
return ruleRegex.exactMatch(key);
|
bool ret = ruleRegex.exactMatch(key);
|
||||||
|
|
||||||
|
// 配置中\.exe$ 在V20中go代码可以匹配以.exe结尾的字符串, Qt中使用\.*exe$匹配以.exe结尾字符串失败,暂时做兼容处理
|
||||||
|
if (!ret && value == "\\.exe$") {
|
||||||
|
ret = key.endsWith(".exe");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool regexMatchIgnoreCase(QString key, QString value) {
|
bool regexMatchIgnoreCase(QString key, QString value) {
|
||||||
QRegExp ruleRegex(value, Qt::CaseInsensitive);
|
QRegExp ruleRegex(value, Qt::CaseInsensitive);
|
||||||
return ruleRegex.exactMatch(key);
|
bool ret = ruleRegex.exactMatch(key);
|
||||||
|
|
||||||
|
// 配置中\.exe$ 在V20中go代码可以匹配以.exe结尾的字符串, Qt中使用\.*exe$匹配以.exe结尾字符串失败,暂时做兼容处理
|
||||||
|
if (!ret && value == "\\.exe$") {
|
||||||
|
QString _key = key.toLower();
|
||||||
|
ret = _key.endsWith(".exe");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RuleValueParse::RuleValueParse()
|
RuleValueParse::RuleValueParse()
|
||||||
: negative(0)
|
: negative(false)
|
||||||
, type(0)
|
, type(0)
|
||||||
, flags(0)
|
, flags(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RuleValueParse::parse(QString parsedKey)
|
bool RuleValueParse::match(const WindowInfoX *winInfo)
|
||||||
{
|
{
|
||||||
|
QString parsedKey = parseRuleKey(const_cast<WindowInfoX *>(winInfo), key);
|
||||||
if (!fn)
|
if (!fn)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return negative ? fn(parsedKey, value) : !fn(parsedKey, value);
|
bool ret = fn(parsedKey, value);
|
||||||
|
return negative ? !ret : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RuleValueParse::match(const WindowInfoX *winInfo)
|
QString RuleValueParse::parseRuleKey(WindowInfoX *winInfo, const QString &ruleKey)
|
||||||
{
|
{
|
||||||
QString parsedKey;
|
ProcessInfo * process = winInfo->getProcess();
|
||||||
|
if (ruleKey == "hasPid") {
|
||||||
|
if (process && process->initWithPid()) {
|
||||||
|
return "t";
|
||||||
|
}
|
||||||
|
return "f";
|
||||||
|
} else if (ruleKey == "exec") {
|
||||||
|
if (process) {
|
||||||
|
// 返回执行文件baseName
|
||||||
|
auto baseName = DFile::base(process->getExe());
|
||||||
|
return baseName.empty() ? "" : baseName.c_str();
|
||||||
|
}
|
||||||
|
} else if (ruleKey == "arg") {
|
||||||
|
if (process) {
|
||||||
|
// 返回命令行参数
|
||||||
|
auto ret = DString::join(process->getArgs(), "");
|
||||||
|
return ret.empty() ? "" : ret.c_str();
|
||||||
|
}
|
||||||
|
} else if (ruleKey == "wmi") {
|
||||||
|
// 窗口实例
|
||||||
|
auto wmClass = winInfo->getWMClass();
|
||||||
|
if (!wmClass.instanceName.empty())
|
||||||
|
return wmClass.instanceName.c_str();
|
||||||
|
} else if (ruleKey == "wmc") {
|
||||||
|
// 窗口类型
|
||||||
|
auto wmClass = winInfo->getWMClass();
|
||||||
|
if (!wmClass.className.empty())
|
||||||
|
return wmClass.className.c_str();
|
||||||
|
} else if (ruleKey == "wmn") {
|
||||||
|
// 窗口名称
|
||||||
|
return winInfo->getWMName();
|
||||||
|
} else if (ruleKey == "wmrole") {
|
||||||
|
// 窗口角色
|
||||||
|
return winInfo->getWmRole();
|
||||||
|
} else {
|
||||||
|
const QString envPrefix = "env.";
|
||||||
|
if (ruleKey.startsWith(envPrefix)) {
|
||||||
|
QString envName = ruleKey.mid(envPrefix.size());
|
||||||
|
if (winInfo->getProcess()) {
|
||||||
|
auto ret = process->getEnv(envName.toStdString());
|
||||||
|
return ret.empty() ? "" : ret.c_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return parse(parsedKey);
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
WindowPatterns::WindowPatterns()
|
WindowPatterns::WindowPatterns()
|
||||||
{
|
{
|
||||||
|
loadWindowPatterns();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief WindowPatterns::match 匹配窗口
|
* @brief WindowPatterns::match 匹配窗口类型
|
||||||
* @param winInfo
|
* @param winInfo
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
QString WindowPatterns::match(WindowInfoX *winInfo)
|
QString WindowPatterns::match(WindowInfoX *winInfo)
|
||||||
{
|
{
|
||||||
for (auto pattern : patterns) {
|
for (auto pattern : patterns) {
|
||||||
|
bool patternOk = true;
|
||||||
|
for (auto rule : pattern.parseRules) {
|
||||||
|
if (!rule.match(winInfo)) {
|
||||||
|
patternOk = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (patternOk) {
|
||||||
|
// 匹配成功
|
||||||
|
return pattern.result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 匹配失败
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,6 +266,7 @@ RuleValueParse WindowPatterns::parseRule(QVector<QString> rule)
|
|||||||
strncpy(orig, ret.original.toStdString().c_str(), size_t(len));
|
strncpy(orig, ret.original.toStdString().c_str(), size_t(len));
|
||||||
switch (orig[1]) {
|
switch (orig[1]) {
|
||||||
case ':':
|
case ':':
|
||||||
|
break;
|
||||||
case '!':
|
case '!':
|
||||||
ret.flags |= parsedFlagNegative;
|
ret.flags |= parsedFlagNegative;
|
||||||
ret.negative = true;
|
ret.negative = true;
|
||||||
@ -227,9 +301,10 @@ RuleValueParse WindowPatterns::parseRule(QVector<QString> rule)
|
|||||||
ret.fn = regexMatchIgnoreCase;
|
ret.fn = regexMatchIgnoreCase;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return ret;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(orig);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,8 +30,8 @@
|
|||||||
|
|
||||||
struct RuleValueParse {
|
struct RuleValueParse {
|
||||||
RuleValueParse();
|
RuleValueParse();
|
||||||
bool parse(QString parsedKey);
|
|
||||||
bool match(const WindowInfoX *winInfo);
|
bool match(const WindowInfoX *winInfo);
|
||||||
|
static QString parseRuleKey(WindowInfoX *winInfo, const QString &ruleKey);
|
||||||
QString key;
|
QString key;
|
||||||
bool negative;
|
bool negative;
|
||||||
bool (*fn)(QString, QString);
|
bool (*fn)(QString, QString);
|
||||||
|
@ -153,7 +153,11 @@ void X11Manager::listenXEventUseXCB()
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注册X11窗口
|
/**
|
||||||
|
* @brief X11Manager::registerWindow 注册X11窗口
|
||||||
|
* @param xid
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
WindowInfoX *X11Manager::registerWindow(XWindow xid)
|
WindowInfoX *X11Manager::registerWindow(XWindow xid)
|
||||||
{
|
{
|
||||||
qInfo() << "registWindow: windowId=" << xid;
|
qInfo() << "registWindow: windowId=" << xid;
|
||||||
@ -249,6 +253,10 @@ void X11Manager::listenRootWindowXEvent()
|
|||||||
handleClientListChanged();
|
handleClientListChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief X11Manager::listenWindowXEvent 监听窗口事件
|
||||||
|
* @param winInfo
|
||||||
|
*/
|
||||||
void X11Manager::listenWindowXEvent(WindowInfoX *winInfo)
|
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;
|
uint32_t eventMask = EventMask::XCB_EVENT_MASK_PROPERTY_CHANGE | EventMask::XCB_EVENT_MASK_STRUCTURE_NOTIFY | EventMask::XCB_EVENT_MASK_VISIBILITY_CHANGE;
|
||||||
|
Loading…
Reference in New Issue
Block a user