feat: 实现Apps服务、Launcher服务、Dock服务
*重写Apps服务,新服务分为org.deepin.daemon.DFWatcher1和org.deepin.daemon.ALRecorder1两个服务 *重写Launcher服务, 新服务名为org.deepin.dde.daemon.Launcher1 *重写Dock服务, 新服务名为org.deepin.dde.daemon.Dock1 *重写部分go-lib接口,保存在src/lib目录, 后续从项目中提出统一存放至开发库 *使用XCB库实现与XServer交互,存放在src/lib目录 *放弃依赖dde-qt-dbus-factory包, 将xml文件生成的静态编译代码存放在frameworkdbus目录 Log: 实现Apps服务、Launcher服务、Dock服务 Task: https://pms.uniontech.com/task-view-109315.html Influence: 无 Change-Id: Ia9676060bfe81ce8d02c48972cc3d3cbaf665a31
This commit is contained in:
169
src/lib/basedir.cpp
Normal file
169
src/lib/basedir.cpp
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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 "basedir.h"
|
||||
#include "dfile.h"
|
||||
#include "dstring.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
BaseDir::BaseDir()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string BaseDir::homeDir()
|
||||
{
|
||||
char *home = getenv("HOME");
|
||||
if (!home)
|
||||
return "";
|
||||
|
||||
return std::string(home) + "/";
|
||||
}
|
||||
|
||||
std::string BaseDir::uerDataDir()
|
||||
{
|
||||
// default $HOME/.local/share
|
||||
std::string home = homeDir();
|
||||
std::string defaultDir = home.size() > 0 ? home + ".local/share/" : "";
|
||||
const char *xdgDataHomePtr = getenv("XDG_DATA_HOME");
|
||||
if (!xdgDataHomePtr)
|
||||
return defaultDir;
|
||||
|
||||
if (!DFile::isAbs(xdgDataHomePtr))
|
||||
return defaultDir;
|
||||
|
||||
return std::string(xdgDataHomePtr) + "/";
|
||||
}
|
||||
|
||||
std::vector<std::string> BaseDir::sysDataDirs()
|
||||
{
|
||||
std::vector<std::string> defaultDirs {"/usr/local/share/", "/usr/share/"};
|
||||
const char *xdgDataDirsPtr = getenv("XDG_DATA_DIRS");
|
||||
if (!xdgDataDirsPtr)
|
||||
return defaultDirs;
|
||||
|
||||
std::string xdgDataDirsStr(xdgDataDirsPtr);
|
||||
std::vector<std::string> xdgDataDirs = DString::splitStr(xdgDataDirsStr, ':');
|
||||
if (xdgDataDirs.size() == 0)
|
||||
return defaultDirs;
|
||||
|
||||
filterNotAbs(xdgDataDirs);
|
||||
addSuffixSlash(xdgDataDirs);
|
||||
return xdgDataDirs;
|
||||
}
|
||||
|
||||
std::string BaseDir::userConfigDir()
|
||||
{
|
||||
// default $HOME/.config
|
||||
std::string defaultDir = homeDir() + ".config/";
|
||||
const char *xdgConfigHomePtr = getenv("XDG_CONFIG_HOME");
|
||||
if (!xdgConfigHomePtr)
|
||||
return defaultDir;
|
||||
|
||||
std::string xdgConfigHome(xdgConfigHomePtr);
|
||||
if (!DFile::isAbs(xdgConfigHome))
|
||||
return defaultDir;
|
||||
|
||||
return xdgConfigHome + "/";
|
||||
}
|
||||
|
||||
std::vector<std::string> BaseDir::sysConfigDirs()
|
||||
{
|
||||
std::vector<std::string> defaultDirs {"/etc/xdg/"};
|
||||
const char *xdgConfigDirsPtr = getenv("XDG_CONFIG_DIRS");
|
||||
if (!xdgConfigDirsPtr)
|
||||
return defaultDirs;
|
||||
|
||||
std::string xdgConfigDirsStr(xdgConfigDirsPtr);
|
||||
std::vector<std::string> xdgConfigDirs = DString::splitStr(xdgConfigDirsStr, ':');
|
||||
if (xdgConfigDirs.size() == 0)
|
||||
return defaultDirs;
|
||||
|
||||
filterNotAbs(xdgConfigDirs);
|
||||
addSuffixSlash(xdgConfigDirs);
|
||||
return xdgConfigDirs;
|
||||
}
|
||||
|
||||
std::string BaseDir::userCacheDir()
|
||||
{
|
||||
std::string home = homeDir();
|
||||
std::string defaultDir = home.size() > 0 ? home + ".cache/" : "";
|
||||
const char *xdgCacheHomePtr = getenv("XDG_CACHE_HOME");
|
||||
if (!xdgCacheHomePtr)
|
||||
return defaultDir;
|
||||
|
||||
std::string xdgCacheHome(xdgCacheHomePtr);
|
||||
if (!DFile::isAbs(xdgCacheHome))
|
||||
return defaultDir;
|
||||
|
||||
return xdgCacheHome + "/";
|
||||
}
|
||||
|
||||
std::string BaseDir::userAppDir()
|
||||
{
|
||||
std::string dataDir = uerDataDir();
|
||||
return dataDir.size() > 0 ? dataDir + "appliations/" : "";
|
||||
}
|
||||
|
||||
std::vector<std::string> BaseDir::sysAppDirs()
|
||||
{
|
||||
auto dataDirs = sysDataDirs();
|
||||
std::vector<std::string> sysAppDirs(dataDirs.size());
|
||||
std::transform(dataDirs.begin(), dataDirs.end(), sysAppDirs.begin(),
|
||||
[](std::string dir) -> std::string {return dir + "applications/";});
|
||||
return sysAppDirs;
|
||||
}
|
||||
|
||||
std::vector<std::string> BaseDir::appDirs()
|
||||
{
|
||||
std::vector<std::string> appDirs = sysAppDirs();
|
||||
appDirs.push_back(userAppDir());
|
||||
return appDirs;
|
||||
}
|
||||
|
||||
std::vector<std::string> BaseDir::autoStartDirs()
|
||||
{
|
||||
std::vector<std::string> autoStartDirs = sysConfigDirs();
|
||||
autoStartDirs.push_back(userConfigDir());
|
||||
std::transform(autoStartDirs.begin(), autoStartDirs.end(), autoStartDirs.begin(),
|
||||
[](std::string dir) -> std::string {return dir + "autostart/";});
|
||||
|
||||
return autoStartDirs;
|
||||
}
|
||||
|
||||
void BaseDir::filterNotAbs(std::vector<std::string> &dirs)
|
||||
{
|
||||
for (auto iter = dirs.begin(); iter != dirs.end();) { // erase element in vector
|
||||
if (!DFile::isAbs(*iter))
|
||||
iter = dirs.erase(iter);
|
||||
else
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
void BaseDir::addSuffixSlash(std::vector<std::string> &dirs)
|
||||
{
|
||||
for (auto &dir : dirs) {
|
||||
if (!DString::endWith(dir, "/"))
|
||||
dir += "/";
|
||||
}
|
||||
}
|
50
src/lib/basedir.h
Normal file
50
src/lib/basedir.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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/>.
|
||||
*/
|
||||
|
||||
#ifndef BASEDIR_H
|
||||
#define BASEDIR_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
// 基础目录类, 目录结尾统一包含斜杠/
|
||||
class BaseDir
|
||||
{
|
||||
public:
|
||||
BaseDir();
|
||||
|
||||
static std::string homeDir();
|
||||
static std::string uerDataDir();
|
||||
static std::vector<std::string> sysDataDirs();
|
||||
static std::string userConfigDir();
|
||||
static std::vector<std::string> sysConfigDirs();
|
||||
static std::string userCacheDir();
|
||||
static std::string userAppDir();
|
||||
static std::vector<std::string> sysAppDirs();
|
||||
static std::vector<std::string> appDirs();
|
||||
static std::vector<std::string> autoStartDirs();
|
||||
|
||||
private:
|
||||
static void filterNotAbs(std::vector<std::string> &dirs);
|
||||
static void addSuffixSlash(std::vector<std::string> &dirs);
|
||||
};
|
||||
|
||||
#endif // BASEDIR_H
|
380
src/lib/desktopinfo.cpp
Normal file
380
src/lib/desktopinfo.cpp
Normal file
@ -0,0 +1,380 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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 "desktopinfo.h"
|
||||
#include "locale.h"
|
||||
#include "unistd.h"
|
||||
#include "dstring.h"
|
||||
#include "dfile.h"
|
||||
#include "basedir.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
#include <dirent.h>
|
||||
|
||||
std::vector<std::string> DesktopInfo::currentDesktops;
|
||||
|
||||
DesktopInfo::DesktopInfo(const std::string &_fileName)
|
||||
: kf(KeyFile())
|
||||
, fileName(_fileName)
|
||||
, isValid(true)
|
||||
{
|
||||
if (!DString::endWith(fileName, ".desktop"))
|
||||
fileName += ".desktop";
|
||||
|
||||
if (!DFile::isAbs(fileName)) {
|
||||
// fileName是文件名,增加目录
|
||||
bool isExisted = false;
|
||||
for (const auto &dir : BaseDir::appDirs()) {
|
||||
fileName = dir + fileName;
|
||||
if (DFile::isExisted(fileName)) {
|
||||
isExisted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isExisted) {
|
||||
isValid = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
kf.loadFile(fileName);
|
||||
|
||||
// check DesktopInfo valid
|
||||
if (fileName.find(".desktop") == std::string::npos)
|
||||
isValid = false;
|
||||
|
||||
std::vector<std::string> mainKeys = kf.getMainKeys();
|
||||
if (mainKeys.size() == 0)
|
||||
isValid = false;
|
||||
|
||||
bool found = std::any_of(mainKeys.begin(), mainKeys.end(),
|
||||
[](const auto &key) {return key == MainSection;});
|
||||
|
||||
if (!found)
|
||||
isValid = false;
|
||||
|
||||
if (kf.getStr(MainSection, KeyType) != TypeApplication)
|
||||
isValid = false;
|
||||
|
||||
name = kf.getLocaleStr(MainSection, KeyName, "");
|
||||
icon = kf.getStr(MainSection, KeyIcon);
|
||||
id = getId();
|
||||
}
|
||||
|
||||
DesktopInfo::~DesktopInfo()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string DesktopInfo::getFileName()
|
||||
{
|
||||
return fileName;
|
||||
}
|
||||
|
||||
bool DesktopInfo::isValidDesktop()
|
||||
{
|
||||
return isValid;
|
||||
}
|
||||
|
||||
bool DesktopInfo::shouldShow()
|
||||
{
|
||||
if (getNoDisplay() || getIsHidden())
|
||||
return false;
|
||||
|
||||
std::vector<std::string> desktopEnvs;
|
||||
return getShowIn(desktopEnvs);
|
||||
}
|
||||
|
||||
bool DesktopInfo::getNoDisplay()
|
||||
{
|
||||
return kf.getBool(MainSection, KeyNoDisplay);
|
||||
}
|
||||
|
||||
bool DesktopInfo::getIsHidden()
|
||||
{
|
||||
return kf.getBool(MainSection, KeyHidden);
|
||||
}
|
||||
|
||||
bool DesktopInfo::getShowIn(std::vector<std::string> desktopEnvs)
|
||||
{
|
||||
if (desktopEnvs.size() == 0) {
|
||||
if (currentDesktops.size() == 0) {
|
||||
const char *env = getenv(envDesktopEnv.c_str());
|
||||
const auto &desktop = DString::splitChars(env, ':');
|
||||
currentDesktops.assign(desktop.begin(), desktop.end());
|
||||
}
|
||||
desktopEnvs.assign(currentDesktops.begin(), currentDesktops.end());
|
||||
}
|
||||
|
||||
std::vector<std::string> onlyShowIn = kf.getStrList(MainSection, KeyOnlyShowIn);
|
||||
std::vector<std::string> notShowIn = kf.getStrList(MainSection, KeyNotShowIn);
|
||||
|
||||
for (const auto &desktop : desktopEnvs) {
|
||||
bool ret = std::any_of(onlyShowIn.begin(), onlyShowIn.end(),
|
||||
[&desktop](const auto &d) {return d == desktop;});
|
||||
if (ret)
|
||||
return true;
|
||||
|
||||
ret = std::any_of(notShowIn.begin(), notShowIn.end(),
|
||||
[&desktop](const auto &d) {return d == desktop;});
|
||||
if (ret)
|
||||
return false;
|
||||
}
|
||||
|
||||
return onlyShowIn.size() == 0;
|
||||
}
|
||||
|
||||
std::string DesktopInfo::getExecutable()
|
||||
{
|
||||
return kf.getStr(MainSection, KeyExec);
|
||||
}
|
||||
|
||||
bool DesktopInfo::isExecutableOk()
|
||||
{
|
||||
// 检查TryExec字段
|
||||
std::string value = getTryExec();
|
||||
std::vector<std::string> parts = DString::splitStr(value, ' ');
|
||||
if (parts.size() > 0 ) {
|
||||
value.assign(parts[0]);
|
||||
DString::delQuote(value);
|
||||
if (strstr(value.c_str(), "/") && DFile::isExisted(value))
|
||||
return true;
|
||||
else
|
||||
return findExecutable(value);
|
||||
}
|
||||
|
||||
// 检查Exec字段
|
||||
value.assign(getExecutable());
|
||||
parts.clear();
|
||||
parts = DString::splitStr(value, ' ');
|
||||
if (parts.size() > 0) {
|
||||
value.assign(parts[0]);
|
||||
DString::delQuote(value);
|
||||
if (strstr(value.c_str(), "/") && DFile::isExisted(value))
|
||||
return true;
|
||||
else
|
||||
return findExecutable(value);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DesktopInfo::isInstalled()
|
||||
{
|
||||
const char *name = fileName.c_str();
|
||||
const char *found = strstr(name, "/applications/");
|
||||
if (!found)
|
||||
return false;
|
||||
|
||||
auto appDirs = BaseDir::appDirs();
|
||||
return std::any_of(appDirs.begin(), appDirs.end(),
|
||||
[&name, &found] (std::string dir) -> bool {return strneq(dir.c_str(), name, size_t(found - name));});
|
||||
}
|
||||
|
||||
std::vector<DesktopAction> DesktopInfo::getActions()
|
||||
{
|
||||
std::vector<DesktopAction> actions;
|
||||
for (const auto &mainKey : kf.getMainKeys()) {
|
||||
if (DString::startWith(mainKey, "Desktop Action")
|
||||
|| DString::endWith(mainKey, "Shortcut Group")) {
|
||||
DesktopAction action;
|
||||
action.name = kf.getLocaleStr(mainKey, KeyName, "");
|
||||
action.exec = kf.getStr(mainKey, KeyExec);
|
||||
actions.push_back(action);
|
||||
}
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
// 使用appId获取DesktopInfo需检查有效性
|
||||
DesktopInfo DesktopInfo::getDesktopInfoById(std::string appId)
|
||||
{
|
||||
if (!DString::endWith(appId, ".desktop"))
|
||||
appId += ".desktop";
|
||||
|
||||
for (const auto & dir : BaseDir::appDirs()) {
|
||||
std::string filePath = dir + appId;
|
||||
//检测文件有效性
|
||||
if (DFile::isExisted(filePath)) {
|
||||
return DesktopInfo(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
return DesktopInfo("");
|
||||
}
|
||||
|
||||
// TryExec is Path to an executable file on disk used to determine if the program is actually installed
|
||||
std::string DesktopInfo::getTryExec()
|
||||
{
|
||||
return kf.getStr(MainSection, KeyTryExec);
|
||||
}
|
||||
|
||||
// 按$PATH路径查找执行文件
|
||||
bool DesktopInfo::findExecutable(std::string &exec)
|
||||
{
|
||||
static const char *path = getenv("PATH");
|
||||
static std::vector<std::string> paths = DString::splitChars(path, ':');
|
||||
return std::any_of(paths.begin(), paths.end(), [&exec](std::string path) {return DFile::isExisted(path + "/" +exec);});
|
||||
}
|
||||
|
||||
// filename must has suffix desktopExt
|
||||
// example:
|
||||
// /usr/share/applications/a.desktop -> a
|
||||
// /usr/share/applications/kde4/a.desktop -> kde4/a
|
||||
// /xxxx/dir/a.desktop -> /xxxx/dir/a
|
||||
std::string DesktopInfo::getId()
|
||||
{
|
||||
if (!id.empty())
|
||||
return id;
|
||||
|
||||
std::string idStr;
|
||||
auto const suffixPos = fileName.find(".desktop");
|
||||
if (suffixPos == std::string::npos)
|
||||
return "";
|
||||
|
||||
idStr = fileName.substr(0, fileName.size() - 8); // trim suffix
|
||||
size_t dirPos = idStr.find("/applications/");
|
||||
if (dirPos == std::string::npos)
|
||||
return "";
|
||||
|
||||
std::string baseDir(idStr.substr(0, dirPos + 14)); // length of "/applications/" is 14
|
||||
std::vector<std::string> appDirs = BaseDir::appDirs();
|
||||
bool installed = std::any_of(appDirs.begin(), appDirs.end(),
|
||||
[&baseDir](const auto &dir) {return dir == baseDir;});
|
||||
|
||||
if (installed) {
|
||||
id = idStr.substr(baseDir.size(), idStr.size());
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
std::string DesktopInfo::getGenericName()
|
||||
{
|
||||
return kf.getLocaleStr(MainSection, KeyGenericName, "");
|
||||
}
|
||||
|
||||
std::string DesktopInfo::getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string DesktopInfo::getIcon()
|
||||
{
|
||||
return icon;
|
||||
}
|
||||
|
||||
std::string DesktopInfo::getCommandLine()
|
||||
{
|
||||
return kf.getStr(MainSection, KeyExec);
|
||||
}
|
||||
|
||||
std::vector<std::string> DesktopInfo::getKeywords()
|
||||
{
|
||||
return kf.getLocaleStrList(MainSection, KeyKeywords, "");
|
||||
}
|
||||
|
||||
std::vector<std::string> DesktopInfo::getCategories()
|
||||
{
|
||||
return kf.getStrList(MainSection, KeyCategories);
|
||||
}
|
||||
|
||||
// class AppsDir
|
||||
AppsDir::AppsDir(const std::string &dirPath)
|
||||
: path(dirPath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AppsDir::~AppsDir()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string AppsDir::getPath()
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
// 获取目录对应的应用名称
|
||||
std::map<std::string, bool> AppsDir::getAppNames()
|
||||
{
|
||||
DIR* dp;
|
||||
struct dirent* ep;
|
||||
|
||||
dp = opendir(path.c_str());
|
||||
if (dp == nullptr)
|
||||
{
|
||||
std::cout << "Couldn't open directory " << path << std::endl;
|
||||
return appNames;
|
||||
}
|
||||
|
||||
while ((ep = readdir(dp))) {
|
||||
if (ep->d_type != DT_REG && ep->d_type != DT_LNK)
|
||||
continue;
|
||||
|
||||
if (!DString::endWith(ep->d_name, ".desktop"))
|
||||
continue;
|
||||
|
||||
appNames.insert({ep->d_name, true});
|
||||
}
|
||||
closedir(dp);
|
||||
|
||||
return appNames;
|
||||
}
|
||||
|
||||
// 获取所有应用信息
|
||||
std::vector<DesktopInfo> AppsDir::getAllDesktopInfos()
|
||||
{
|
||||
std::map<std::string, bool> recoder;
|
||||
std::vector<DesktopInfo> desktopInfos;
|
||||
|
||||
for (auto dir : BaseDir::appDirs()) {
|
||||
AppsDir appsDir(dir);
|
||||
std::map<std::string, bool> appNames = appsDir.getAppNames();
|
||||
if (appNames.size() == 0)
|
||||
continue;
|
||||
|
||||
for (const auto &iter : appNames) {
|
||||
if (recoder.find(iter.first) != recoder.end())
|
||||
continue;
|
||||
|
||||
std::string filePath = dir + iter.first;
|
||||
DesktopInfo desktopInfo(filePath);
|
||||
if (!desktopInfo.isValidDesktop())
|
||||
continue;
|
||||
|
||||
if (!desktopInfo.shouldShow())
|
||||
continue;
|
||||
|
||||
desktopInfos.push_back(std::move(desktopInfo));
|
||||
recoder[iter.first] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return desktopInfos;
|
||||
}
|
||||
|
121
src/lib/desktopinfo.h
Normal file
121
src/lib/desktopinfo.h
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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/>.
|
||||
*/
|
||||
|
||||
#ifndef DESKTOPINFO_H
|
||||
#define DESKTOPINFO_H
|
||||
|
||||
#include "keyfile.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
const std::string MainSection = "Desktop Entry";
|
||||
const std::string KeyType = "Type";
|
||||
const std::string KeyVersion = "Version";
|
||||
const std::string KeyName = "Name";
|
||||
const std::string KeyGenericName = "GenericName";
|
||||
const std::string KeyNoDisplay = "NoDisplay";
|
||||
const std::string KeyComment = "Comment";
|
||||
const std::string KeyIcon = "Icon";
|
||||
const std::string KeyHidden = "Hidden";
|
||||
const std::string KeyOnlyShowIn = "OnlyShowIn";
|
||||
const std::string KeyNotShowIn = "NotShowIn";
|
||||
const std::string KeyTryExec = "TryExec";
|
||||
const std::string KeyExec = "Exec";
|
||||
const std::string KeyPath = "Path";
|
||||
const std::string KeyTerminal = "Terminal";
|
||||
const std::string KeyMimeType = "MimeType";
|
||||
const std::string KeyCategories = "Categories";
|
||||
const std::string KeyKeywords = "Keywords";
|
||||
const std::string KeyStartupNotify = "StartupNotify";
|
||||
const std::string KeyStartupWMClass = "StartupWMClass";
|
||||
const std::string KeyURL = "URL";
|
||||
const std::string KeyActions = "Actions";
|
||||
const std::string KeyDBusActivatable = "DBusActivatable";
|
||||
|
||||
const std::string TypeApplication = "Application";
|
||||
const std::string TypeLink = "Link";
|
||||
const std::string TypeDirectory = "Directory";
|
||||
|
||||
const std::string envDesktopEnv = "XDG_CURRENT_DESKTOP";
|
||||
|
||||
typedef struct DesktopAction {
|
||||
std::string section;
|
||||
std::string name;
|
||||
std::string exec;
|
||||
} DesktopAction;
|
||||
|
||||
// 应用Desktop信息类
|
||||
class DesktopInfo {
|
||||
public:
|
||||
explicit DesktopInfo(const std::string &_fileName);
|
||||
~DesktopInfo();
|
||||
|
||||
std::string getFileName();
|
||||
std::string getExecutable();
|
||||
bool isValidDesktop();
|
||||
bool shouldShow();
|
||||
bool getNoDisplay();
|
||||
bool getIsHidden();
|
||||
bool getShowIn(std::vector<std::string> desktopEnvs);
|
||||
bool isExecutableOk();
|
||||
bool isInstalled();
|
||||
std::vector<DesktopAction> getActions();
|
||||
static DesktopInfo getDesktopInfoById(std::string appId);
|
||||
|
||||
std::string getId();
|
||||
std::string getGenericName();
|
||||
std::string getName();
|
||||
std::string getIcon();
|
||||
std::string getCommandLine();
|
||||
std::vector<std::string> getKeywords();
|
||||
std::vector<std::string> getCategories();
|
||||
|
||||
KeyFile kf;
|
||||
|
||||
private:
|
||||
std::string getTryExec();
|
||||
bool findExecutable(std::string &exec);
|
||||
std::string fileName;
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::string icon;
|
||||
std::string overRideExec;
|
||||
bool isValid;
|
||||
static std::vector<std::string> currentDesktops;
|
||||
};
|
||||
|
||||
// 应用目录类
|
||||
class AppsDir {
|
||||
public:
|
||||
explicit AppsDir(const std::string &dirPath);
|
||||
~AppsDir();
|
||||
|
||||
std::string getPath();
|
||||
std::map<std::string, bool> getAppNames();
|
||||
static std::vector<DesktopInfo> getAllDesktopInfos();
|
||||
|
||||
private:
|
||||
std::string path;
|
||||
std::map<std::string, bool> appNames;
|
||||
};
|
||||
|
||||
#endif // DESKTOPINFO_H
|
79
src/lib/dfile.cpp
Normal file
79
src/lib/dfile.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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 "dfile.h"
|
||||
#include "macro.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
|
||||
DFile::DFile()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool DFile::isAbs(std::string file)
|
||||
{
|
||||
char resolved_path[MAX_FILEPATH_LEN];
|
||||
if (realpath(file.c_str(), resolved_path)) {
|
||||
std::string filePath(resolved_path);
|
||||
if (filePath == file)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DFile::isExisted(std::string file)
|
||||
{
|
||||
return !access(file.c_str(), F_OK);
|
||||
}
|
||||
|
||||
std::string DFile::dir(std::string file)
|
||||
{
|
||||
std::string ret;
|
||||
if (isAbs(file)) {
|
||||
size_t pos = file.find_last_of("/");
|
||||
if (pos != std::string::npos) {
|
||||
ret.assign(file, 0, pos + 1); // 包含结尾斜杠/
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string DFile::base(std::string file)
|
||||
{
|
||||
std::string ret;
|
||||
if (strstr(file.c_str(), "/")) { // 包含路径
|
||||
size_t pos = file.find_last_of("/");
|
||||
if (pos != std::string::npos) {
|
||||
ret.assign(file, pos + 1, file.size() - pos); // 去除路径
|
||||
}
|
||||
}
|
||||
|
||||
size_t pos = file.find_last_of("."); // 去除后缀
|
||||
if (pos != std::string::npos) {
|
||||
ret.assign(file, 0, pos + 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
37
src/lib/dfile.h
Normal file
37
src/lib/dfile.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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/>.
|
||||
*/
|
||||
|
||||
#ifndef DFILE_H
|
||||
#define DFILE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
class DFile
|
||||
{
|
||||
public:
|
||||
explicit DFile();
|
||||
static bool isAbs(std::string file);
|
||||
static bool isExisted(std::string file);
|
||||
static std::string dir(std::string file);
|
||||
static std::string base(std::string file);
|
||||
};
|
||||
|
||||
#endif // DFILE_H
|
210
src/lib/dlocale.cpp
Normal file
210
src/lib/dlocale.cpp
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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 "dlocale.h"
|
||||
#include "dstring.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define ComponentCodeset 1
|
||||
#define ComponentTerritory 2
|
||||
#define ComponentModifier 4
|
||||
|
||||
#define MAXLINELEN 256
|
||||
|
||||
const char *aliasFile = "/usr/share/locale/locale.alias";
|
||||
const char charUscore = '_';
|
||||
const char charDot = '.';
|
||||
const char charAt = '@';
|
||||
|
||||
Locale::Locale()
|
||||
{
|
||||
pthread_mutex_init(&languageNames.mutex, nullptr);
|
||||
|
||||
// init aliases
|
||||
FILE *fp = fopen(aliasFile, "r");
|
||||
if (fp) {
|
||||
|
||||
char data[MAXLINELEN] = {0};
|
||||
std::string line;
|
||||
std::vector<std::string> parts;
|
||||
while (fgets(data, MAXLINELEN, fp)) {
|
||||
char *start = &data[0];
|
||||
char *end = start;
|
||||
|
||||
// 移除行首
|
||||
while (strneq(start, " ", 1) || strneq(start, "\t", 1))
|
||||
start++;
|
||||
|
||||
// 过滤注释行和空行
|
||||
if (strneq(start, "#", 1) || strneq(start, "\n", 1))
|
||||
continue;
|
||||
|
||||
while (!strneq(end, "\n", 1))
|
||||
end++;
|
||||
|
||||
// 移除行尾
|
||||
while (strneq(end, "\n", 1) || strneq(end, "\r", 1)
|
||||
|| strneq(end, " ", 1) || strneq(end, "\t", 1))
|
||||
end--;
|
||||
|
||||
line.assign(start, ulong(end - start + 1));
|
||||
parts = DString::splitStr(line, ' ');
|
||||
// 使用\t分割
|
||||
if (parts.size() != 2)
|
||||
parts = DString::splitStr(line, '\t');
|
||||
|
||||
if (parts.size() == 2) {
|
||||
aliases[parts[0]] = parts[1];
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
// wayland environment is useful?
|
||||
// ExplodeLocale Break an X/Open style locale specification into components
|
||||
Locale::Components Locale::explodeLocale(std::string locale)
|
||||
{
|
||||
Components cmp;
|
||||
std::vector<std::string> parts;
|
||||
if (locale.find(charAt) != std::string::npos) {
|
||||
parts = DString::splitStr(locale, charAt);
|
||||
if (parts.size() == 2) {
|
||||
cmp.modifier = parts[1];
|
||||
locale = parts[0];
|
||||
cmp.mask |= ComponentModifier;
|
||||
}
|
||||
}
|
||||
|
||||
if (locale.find(charDot) != std::string::npos) {
|
||||
parts = DString::splitStr(locale, charDot);
|
||||
if (parts.size() == 2) {
|
||||
cmp.codeset = parts[1];
|
||||
locale = locale[0];
|
||||
cmp.mask |= ComponentCodeset;
|
||||
}
|
||||
}
|
||||
|
||||
if (locale.find(charUscore) != std::string::npos) {
|
||||
parts = DString::splitStr(locale, charUscore);
|
||||
if (parts.size() == 2) {
|
||||
cmp.territory = parts[1];
|
||||
locale = parts[0];
|
||||
cmp.mask |= ComponentTerritory;
|
||||
}
|
||||
}
|
||||
|
||||
cmp.language = locale;
|
||||
return cmp;
|
||||
}
|
||||
|
||||
std::string Locale::guessCategoryValue(std::string categoryName)
|
||||
{
|
||||
// The highest priority value is the 'LANGUAGE' environment
|
||||
// variable. This is a GNU extension.
|
||||
const char *language = getenv("LANGUAGE");
|
||||
if (language)
|
||||
return language;
|
||||
|
||||
// Setting of LC_ALL overwrites all other.
|
||||
const char *lcAll = getenv("LC_ALL");
|
||||
if (lcAll)
|
||||
return lcAll;
|
||||
|
||||
// Next comes the name of the desired category.
|
||||
const char *name = getenv(categoryName.c_str());
|
||||
if (name)
|
||||
return name;
|
||||
|
||||
// Last possibility is the LANG environment variable.
|
||||
const char *lang = getenv("LANG");
|
||||
if (lang)
|
||||
return lang;
|
||||
|
||||
return "C";
|
||||
}
|
||||
|
||||
std::string Locale::unaliasLang(std::string lang)
|
||||
{
|
||||
if (aliases.find(lang) != aliases.end())
|
||||
return aliases[lang];
|
||||
else
|
||||
return lang;
|
||||
}
|
||||
|
||||
// wayland environment is useful?
|
||||
/*
|
||||
* Compute all interesting variants for a given locale name -
|
||||
* by stripping off different components of the value.
|
||||
*
|
||||
* For simplicity, we assume that the locale is in
|
||||
* X/Open format: language[_territory][.codeset][@modifier]
|
||||
*/
|
||||
std::vector<std::string> Locale::getLocaleVariants(const std::string &locale)
|
||||
{
|
||||
auto cmp = explodeLocale(locale);
|
||||
uint mask = cmp.mask;
|
||||
std::vector<std::string> variants;
|
||||
for (uint i = 0; i <= mask; i++) {
|
||||
uint j = mask - i;
|
||||
//if ((j & ^mask) == 0) {
|
||||
std::string var(cmp.language);
|
||||
if (j & ComponentTerritory)
|
||||
var = var + charUscore + cmp.territory;
|
||||
if (j & ComponentCodeset)
|
||||
var = var + charDot + cmp.codeset;
|
||||
if (j & ComponentModifier)
|
||||
var = var + charAt + cmp.modifier;
|
||||
|
||||
variants.push_back(var);
|
||||
//}
|
||||
}
|
||||
|
||||
return variants;
|
||||
}
|
||||
|
||||
std::vector<std::string> Locale::getLanguageNames()
|
||||
{
|
||||
std::vector<std::string> names;
|
||||
std::string value(guessCategoryValue("LC_MESSAGES"));
|
||||
if (value.empty()) {
|
||||
names.push_back(value);
|
||||
return names;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&languageNames.mutex);
|
||||
if (languageNames.language != value) {
|
||||
languageNames.language = value;
|
||||
languageNames.names.clear();
|
||||
std::vector<std::string> langs = DString::splitStr(value, ':');
|
||||
for (const auto & lang : langs) {
|
||||
std::vector<std::string> localeVariant = getLocaleVariants(unaliasLang(lang));
|
||||
for (const auto & var : localeVariant)
|
||||
languageNames.names.push_back(var);
|
||||
}
|
||||
languageNames.names.push_back("C");
|
||||
}
|
||||
pthread_mutex_unlock(&languageNames.mutex);
|
||||
|
||||
return languageNames.names;
|
||||
}
|
66
src/lib/dlocale.h
Normal file
66
src/lib/dlocale.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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/>.
|
||||
*/
|
||||
|
||||
#ifndef LOCALE_H
|
||||
#define LOCALE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
// 本地化类
|
||||
class Locale {
|
||||
struct LanguageNameCache {
|
||||
std::string language;
|
||||
std::vector<std::string> names;
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
struct Components {
|
||||
Components() : mask(0) {} // 数字必须初始化
|
||||
std::string language;
|
||||
std::string territory;
|
||||
std::string codeset;
|
||||
std::string modifier;
|
||||
uint mask;
|
||||
};
|
||||
|
||||
public:
|
||||
std::vector<std::string> getLocaleVariants(const std::string &locale);
|
||||
std::vector<std::string> getLanguageNames();
|
||||
static inline Locale *instance() {
|
||||
static Locale instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
private:
|
||||
Locale();
|
||||
Locale(const Locale &);
|
||||
Locale& operator= (const Locale &);
|
||||
|
||||
Components explodeLocale(std::string locale);
|
||||
std::string guessCategoryValue(std::string categoryName);
|
||||
std::string unaliasLang(std::string);
|
||||
std::map<std::string, std::string> aliases;
|
||||
LanguageNameCache languageNames;
|
||||
};
|
||||
|
||||
#endif
|
147
src/lib/dstring.cpp
Normal file
147
src/lib/dstring.cpp
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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 "dstring.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
DString::DString()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DString::~DString()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::vector<std::string> DString::splitChars(const char *cs, char c)
|
||||
{
|
||||
assert(cs);
|
||||
|
||||
std::vector<std::string> ret;
|
||||
unsigned long idx = 0;
|
||||
unsigned long size = strlen(cs);
|
||||
bool found = false;
|
||||
std::string sub;
|
||||
for (unsigned long i=0; i < size; i++) {
|
||||
if (!strneq(&cs[i], &c, 1))
|
||||
continue;
|
||||
|
||||
sub.assign(cs, found ? idx+1:idx, found ? i-idx-1:i-idx);
|
||||
if (idx < i && !sub.empty()) {
|
||||
ret.push_back(sub);
|
||||
}
|
||||
idx = i;
|
||||
found = true;
|
||||
}
|
||||
sub.assign(cs, found ? idx+1:idx, found ? size-idx-1:size-idx);
|
||||
if (idx < size && !sub.empty()) {
|
||||
ret.push_back(sub);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::string> DString::splitStr(const std::string &str, char c)
|
||||
{
|
||||
return splitChars(str.c_str(), c);
|
||||
}
|
||||
|
||||
std::vector<std::string> DString::splitVectorChars(const std::vector<char> &content, size_t length, char c)
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
size_t pos = 0;
|
||||
bool hasChar = true;
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if (content[i] == c && i > pos && hasChar) {
|
||||
std::string str;
|
||||
for (size_t j = pos; j <= i; j++) {
|
||||
str += std::string(1, content[j]);
|
||||
}
|
||||
ret.push_back(str);
|
||||
pos = i + 1;
|
||||
hasChar = false;
|
||||
} else {
|
||||
hasChar = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool DString::startWith(const char *chars, const char *prefix)
|
||||
{
|
||||
assert(chars);
|
||||
assert(prefix);
|
||||
|
||||
size_t len;
|
||||
len = strlen(prefix);
|
||||
return strneq(chars, prefix, len);
|
||||
}
|
||||
|
||||
bool DString::startWith(const std::string &str, const std::string &prefix)
|
||||
{
|
||||
return startWith(str.c_str(), prefix.c_str());
|
||||
}
|
||||
|
||||
bool DString::endWith(const char *chars, const char *suffix)
|
||||
{
|
||||
assert(chars);
|
||||
assert(suffix);
|
||||
|
||||
size_t charsLen = strlen(chars);
|
||||
size_t suffixLen = strlen(suffix);
|
||||
|
||||
if (charsLen == 0 || charsLen < suffixLen)
|
||||
return false;
|
||||
|
||||
return memcmp(chars + charsLen - suffixLen, suffix, suffixLen) == 0;
|
||||
}
|
||||
|
||||
bool DString::endWith(const std::string &str, const std::string &suffix)
|
||||
{
|
||||
return endWith(str.c_str(), suffix.c_str());
|
||||
}
|
||||
|
||||
char *DString::delQuote(const char *chars)
|
||||
{
|
||||
char *data = nullptr;
|
||||
if (!chars)
|
||||
return data;
|
||||
|
||||
if (strneq(chars, "\"", 1) && strneq(chars + strlen(chars) - 1, "\"", 1)) {
|
||||
data = static_cast<char *>(calloc(1, strlen(chars) - 2));
|
||||
memcpy(data, chars + 1, strlen(chars) - 1);
|
||||
} else {
|
||||
data = static_cast<char *>(calloc(1, strlen(chars) + 1));
|
||||
memcpy(data, chars, strlen(chars) + 1);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void DString::delQuote(std::string &str)
|
||||
{
|
||||
while (*str.begin() == '\"' && *str.rbegin() == '\"')
|
||||
str.assign(str.substr(1, str.size() - 2));
|
||||
}
|
||||
|
56
src/lib/dstring.h
Normal file
56
src/lib/dstring.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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/>.
|
||||
*/
|
||||
|
||||
#ifndef DSTRING_H
|
||||
#define DSTRING_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
#define streq(a,b) (strcmp((a),(b)) == 0)
|
||||
#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
|
||||
#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0)
|
||||
#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
|
||||
|
||||
// 字符串操作
|
||||
class DString
|
||||
{
|
||||
public:
|
||||
DString();
|
||||
~DString();
|
||||
|
||||
// 字符串拆分
|
||||
static std::vector<std::string> splitChars(const char *cs, char c);
|
||||
static std::vector<std::string> splitStr(const std::string &str, char c);
|
||||
static std::vector<std::string> splitVectorChars(const std::vector<char> &content, size_t length, char c);
|
||||
// 字符串前缀判断
|
||||
static bool startWith(const char *chars, const char *prefix);
|
||||
static bool startWith(const std::string &str, const std::string &prefix);
|
||||
// 字符后缀判断
|
||||
static bool endWith(const char *chars, const char *suffix);
|
||||
static bool endWith(const std::string &str, const std::string &suffix);
|
||||
// 去除首尾引用
|
||||
static char *delQuote(const char *chars);
|
||||
static void delQuote(std::string &str);
|
||||
};
|
||||
|
||||
#endif // DSTRING_H
|
289
src/lib/keyfile.cpp
Normal file
289
src/lib/keyfile.cpp
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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 "keyfile.h"
|
||||
#include "dlocale.h"
|
||||
#include "dstring.h"
|
||||
#include "macro.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
KeyFile::KeyFile(char separtor)
|
||||
: fp(nullptr)
|
||||
, modified(false)
|
||||
, listSeparator(separtor)
|
||||
{
|
||||
}
|
||||
|
||||
KeyFile::~KeyFile()
|
||||
{
|
||||
if (fp) {
|
||||
fclose(fp);
|
||||
fp = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool KeyFile::getBool(const std::string §ion, const std::string &key, bool defaultValue)
|
||||
{
|
||||
if (mainKeyMap.find(section) == mainKeyMap.end())
|
||||
return false;
|
||||
|
||||
std::string valueStr = mainKeyMap[section][key];
|
||||
bool value = defaultValue;
|
||||
if (valueStr == "true")
|
||||
value = true;
|
||||
else if (valueStr == "false")
|
||||
value = false;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// TODO
|
||||
std::vector<bool> KeyFile::getBoolList(const std::string §ion, const std::string &key, bool defaultValue)
|
||||
{
|
||||
std::vector<bool> tmp;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
int KeyFile::getInt(const std::string §ion, const std::string &key, int defaultValue)
|
||||
{
|
||||
if (mainKeyMap.find(section) == mainKeyMap.end())
|
||||
return defaultValue;
|
||||
|
||||
std::string valueStr = mainKeyMap[section][key];
|
||||
int value;
|
||||
try {
|
||||
value = std::stoi(valueStr);
|
||||
} catch (std::invalid_argument&) {
|
||||
value = defaultValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// TODO
|
||||
std::vector<int> KeyFile::getIntList(const std::string §ion, const std::string &key, int defaultValue)
|
||||
{
|
||||
std::vector<int> tmp;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
// TODO
|
||||
int64_t KeyFile::getInt64(const std::string §ion, const std::string &key, int64_t defaultValue)
|
||||
{
|
||||
return int64_t(0);
|
||||
}
|
||||
|
||||
// TODO
|
||||
float KeyFile::getFloat(const std::string §ion, const std::string &key, float defaultValue)
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
std::string KeyFile::getStr(const std::string §ion, const std::string &key, std::string defaultValue)
|
||||
{
|
||||
if (mainKeyMap.find(section) == mainKeyMap.end())
|
||||
return defaultValue;
|
||||
|
||||
std::string valueStr = mainKeyMap[section][key];
|
||||
if (valueStr.empty())
|
||||
valueStr = defaultValue;
|
||||
|
||||
return valueStr;
|
||||
}
|
||||
|
||||
bool KeyFile::containKey(const std::string §ion, const std::string &key)
|
||||
{
|
||||
if (mainKeyMap.find(section) == mainKeyMap.end())
|
||||
return false;
|
||||
|
||||
return mainKeyMap[section].find(key) != mainKeyMap[section].end();
|
||||
}
|
||||
|
||||
std::string KeyFile::getLocaleStr(const std::string §ion, const std::string &key, std::string defaultLocale)
|
||||
{
|
||||
std::vector<std::string> languages = defaultLocale.empty()
|
||||
? Locale::instance()->getLanguageNames()
|
||||
: Locale::instance()->getLocaleVariants(defaultLocale);
|
||||
|
||||
std::string translated;
|
||||
for (const auto &lang : languages) {
|
||||
translated.assign(getStr(section, key + "[" + lang + "]"));
|
||||
if (!translated.empty())
|
||||
return translated;
|
||||
}
|
||||
|
||||
// NOTE: not support key Gettext-Domain
|
||||
// fallback to default key
|
||||
return getStr(section, key);
|
||||
}
|
||||
|
||||
std::vector<std::string> KeyFile::getStrList(const std::string §ion, const std::string &key)
|
||||
{
|
||||
std::string value = getStr(section, key);
|
||||
return DString::splitStr(value, listSeparator);
|
||||
}
|
||||
|
||||
std::vector<std::string> KeyFile::getLocaleStrList(const std::string §ion, const std::string &key, std::string defaultLocale)
|
||||
{
|
||||
std::vector<std::string> languages = defaultLocale.empty()
|
||||
? Locale::instance()->getLanguageNames()
|
||||
: Locale::instance()->getLocaleVariants(defaultLocale);
|
||||
|
||||
std::vector<std::string> translated;
|
||||
for (const auto &lang : languages) {
|
||||
translated = getStrList(section, key + "[" + lang + "]");
|
||||
if (translated.size() > 0)
|
||||
return translated;
|
||||
}
|
||||
|
||||
//fallback to default key
|
||||
return getStrList(section, key);
|
||||
}
|
||||
|
||||
// 修改keyfile内容
|
||||
void KeyFile::setKey(const std::string §ion, const std::string &key, const std::string &value)
|
||||
{
|
||||
if (mainKeyMap.find(section) == mainKeyMap.end())
|
||||
mainKeyMap.insert({section, KeyMap()});
|
||||
|
||||
mainKeyMap[section].insert({key, value});
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
bool KeyFile::saveToFile(const std::string &filePath)
|
||||
{
|
||||
FILE *sfp = fopen(filePath.data(), "w+");
|
||||
if (!sfp)
|
||||
return false;
|
||||
|
||||
|
||||
for (const auto &im : mainKeyMap) {
|
||||
const auto &keyMap = im.second;
|
||||
std::string section = "[" + im.first + "]\n";
|
||||
fputs(section.c_str(), sfp);
|
||||
for (const auto &ik : keyMap) {
|
||||
std::string kv = ik.first + "=" + ik.second + "\n";
|
||||
fputs(kv.c_str(), sfp);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(sfp);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KeyFile::loadFile(const std::string &filePath)
|
||||
{
|
||||
mainKeyMap.clear();
|
||||
if (fp) {
|
||||
fclose(fp);
|
||||
fp = nullptr;
|
||||
}
|
||||
|
||||
std::string lastSection;
|
||||
fp = fopen(filePath.data(), "r");
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
char line[MAX_LINE_LEN] = {0};
|
||||
while (fgets(line, MAX_LINE_LEN, fp)) {
|
||||
char *start = &line[0];
|
||||
char *end = start;
|
||||
while (!strneq(end, "\0", 1))
|
||||
end++;
|
||||
|
||||
end--; // 返回'\0'前一个字符
|
||||
|
||||
// 移除行首
|
||||
while (strneq(start, " ", 1) || strneq(start, "\t", 1))
|
||||
start++;
|
||||
|
||||
// 过滤注释行
|
||||
if (strneq(start, "#", 1))
|
||||
continue;
|
||||
|
||||
// 移除行尾
|
||||
while (strneq(end, "\n", 1) || strneq(end, "\r", 1)
|
||||
|| strneq(end, " ", 1) || strneq(end, "\t", 1))
|
||||
end--;
|
||||
|
||||
char *lPos = strchr(start, '[');
|
||||
char *rPos = strchr(start, ']');
|
||||
if (lPos && rPos && rPos - lPos > 0 && lPos == start && rPos == end) {
|
||||
// 主键
|
||||
std::string section(lPos + 1, size_t(rPos - lPos - 1));
|
||||
mainKeyMap.insert({section, KeyMap()});
|
||||
lastSection = section;
|
||||
} else {
|
||||
char *equal = strchr(start, '=');
|
||||
if (!equal)
|
||||
continue;
|
||||
|
||||
// 文件格式错误
|
||||
if (lastSection.empty()) {
|
||||
std::cout << "failed to load file " << filePath << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 子键
|
||||
std::string key(start, size_t(equal - start));
|
||||
std::string value(equal + 1, size_t(end - equal));
|
||||
for (auto &iter : mainKeyMap) {
|
||||
if (iter.first != lastSection)
|
||||
continue;
|
||||
|
||||
iter.second[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
fp = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> KeyFile::getMainKeys()
|
||||
{
|
||||
std::vector<std::string> mainKeys;
|
||||
for (const auto &iter : mainKeyMap)
|
||||
mainKeys.push_back(iter.first);
|
||||
|
||||
return mainKeys;
|
||||
}
|
||||
|
||||
void KeyFile::print()
|
||||
{
|
||||
std::cout << "sectionMap: " << std::endl;
|
||||
for (auto sectionMap : mainKeyMap) {
|
||||
std::cout << "section=" << sectionMap.first << std::endl;
|
||||
KeyMap keyMap = sectionMap.second;
|
||||
|
||||
for (auto iter : keyMap) {
|
||||
std::cout << iter.first << "=" << iter.second << std::endl;
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
67
src/lib/keyfile.h
Normal file
67
src/lib/keyfile.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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/>.
|
||||
*/
|
||||
|
||||
#ifndef KEYFILE_H
|
||||
#define KEYFILE_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
typedef std::map<std::string, std::string> KeyMap;
|
||||
typedef std::map<std::string, KeyMap> MainKeyMap;
|
||||
|
||||
// 解析ini、desktop文件类
|
||||
class KeyFile
|
||||
{
|
||||
public:
|
||||
explicit KeyFile(char separtor = ';');
|
||||
~KeyFile();
|
||||
|
||||
bool getBool(const std::string §ion, const std::string &key, bool defaultValue = false);
|
||||
std::vector<bool> getBoolList(const std::string §ion, const std::string &key, bool defaultValue = false);
|
||||
int getInt(const std::string §ion, const std::string &key, int defaultValue = 0);
|
||||
std::vector<int> getIntList(const std::string §ion, const std::string &key, int defaultValue = 0);
|
||||
int64_t getInt64(const std::string §ion, const std::string &key, int64_t defaultValue = 0);
|
||||
float getFloat(const std::string §ion, const std::string &key, float defaultValue = 0);
|
||||
std::string getStr(const std::string §ion, const std::string &key, std::string defaultValue = "");
|
||||
bool containKey(const std::string §ion, const std::string &key);
|
||||
std::string getLocaleStr(const std::string §ion, const std::string &key, std::string defaultLocale = "");
|
||||
std::vector<std::string> getStrList(const std::string §ion, const std::string &key);
|
||||
std::vector<std::string> getLocaleStrList(const std::string §ion, const std::string &key, std::string defaultLocale = "");
|
||||
|
||||
void setKey(const std::string §ion, const std::string &key, const std::string &value);
|
||||
bool saveToFile(const std::string &filePath);
|
||||
bool loadFile(const std::string &filePath);
|
||||
std::vector<std::string> getMainKeys();
|
||||
|
||||
// for test
|
||||
void print();
|
||||
|
||||
private:
|
||||
MainKeyMap mainKeyMap; // section -> key : value
|
||||
std::string filePath;
|
||||
FILE *fp;
|
||||
bool modified;
|
||||
char listSeparator;
|
||||
};
|
||||
|
||||
#endif // KEYFILE_H
|
72
src/lib/lang.h
Normal file
72
src/lib/lang.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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/>.
|
||||
*/
|
||||
|
||||
#ifndef LANG_H
|
||||
#define LANG_H
|
||||
|
||||
#include "dstring.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
// 返回用户语言,参见man gettext
|
||||
inline std::vector<std::string> queryLangs() {
|
||||
std::vector<std::string> ret;
|
||||
const char *lcAll = getenv("LC_ALL");
|
||||
const char *lcMessage = getenv("LC_MESSAGE");
|
||||
const char *language = getenv("LANGUAGE");
|
||||
const char *lang = getenv("LANG");
|
||||
|
||||
auto cutOff = [](std::string str)->std::string {
|
||||
size_t idx = str.find(".");
|
||||
if (idx == std::string::npos)
|
||||
return str;
|
||||
|
||||
return std::string(str).substr(0, idx);
|
||||
};
|
||||
|
||||
if (lcAll && std::string(lcAll) != "C"
|
||||
&& language && std::string(language) != "")
|
||||
{
|
||||
std::vector<std::string> splits = DString::splitChars(language, ':');
|
||||
for (const auto &l : splits) {
|
||||
ret.push_back(cutOff(l));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (lcAll && std::string(lcAll) != "")
|
||||
ret.push_back(cutOff(lcAll));
|
||||
|
||||
if (lcMessage && std::string(lcMessage) != "")
|
||||
ret.push_back(cutOff(lcMessage));
|
||||
|
||||
if (lang && std::string(lang) != "")
|
||||
ret.push_back(cutOff(lang));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#endif // LANG_H
|
72
src/lib/lang.hpp
Normal file
72
src/lib/lang.hpp
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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/>.
|
||||
*/
|
||||
|
||||
#ifndef LANG_H
|
||||
#define LANG_H
|
||||
|
||||
#include "dstring.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
// 返回用户语言,参见man gettext
|
||||
inline std::vector<std::string> queryLangs() {
|
||||
std::vector<std::string> ret;
|
||||
const char *lcAll = getenv("LC_ALL");
|
||||
const char *lcMessage = getenv("LC_MESSAGE");
|
||||
const char *language = getenv("LANGUAGE");
|
||||
const char *lang = getenv("LANG");
|
||||
|
||||
auto cutOff = [](std::string str)->std::string {
|
||||
size_t idx = str.find(".");
|
||||
if (idx == std::string::npos)
|
||||
return str;
|
||||
|
||||
return std::string(str).substr(0, idx);
|
||||
};
|
||||
|
||||
if (lcAll && std::string(lcAll) != "C"
|
||||
&& language && std::string(language) != "")
|
||||
{
|
||||
std::vector<std::string> splits = DString::splitChars(language, ':');
|
||||
for (const auto &l : splits) {
|
||||
ret.push_back(cutOff(l));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (lcAll && std::string(lcAll) != "")
|
||||
ret.push_back(cutOff(lcAll));
|
||||
|
||||
if (lcMessage && std::string(lcMessage) != "")
|
||||
ret.push_back(cutOff(lcMessage));
|
||||
|
||||
if (lang && std::string(lang) != "")
|
||||
ret.push_back(cutOff(lang));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#endif // LANG_H
|
31
src/lib/macro.h
Normal file
31
src/lib/macro.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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/>.
|
||||
*/
|
||||
|
||||
#ifndef MACRO_H
|
||||
#define MACRO_H
|
||||
|
||||
#define _likely_(x) (__builtin_expect(!!(x), 1))
|
||||
#define _unlikely_(x) (__builtin_expect(!!(x), 0))
|
||||
|
||||
#define MAX_FILEPATH_LEN 256
|
||||
#define MAX_LINE_LEN 256
|
||||
|
||||
#endif // MACRO_H
|
188
src/lib/process.cpp
Normal file
188
src/lib/process.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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 "process.h"
|
||||
#include "macro.h"
|
||||
#include "dstring.h"
|
||||
#include "dfile.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define FILECONTENLEN 2048
|
||||
|
||||
Process::Process()
|
||||
: pid(0)
|
||||
, ppid(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Process::Process(int _pid)
|
||||
: pid(_pid)
|
||||
, ppid(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool Process::isExist()
|
||||
{
|
||||
std::string procDir = "/proc/" + std::to_string(pid);
|
||||
return DFile::isExisted(procDir);
|
||||
}
|
||||
|
||||
std::vector<std::string> Process::getCmdLine()
|
||||
{
|
||||
if (cmdLine.size() == 0) {
|
||||
std::string cmdlineFile = getFile("cmdline");
|
||||
cmdLine = readFile(cmdlineFile);
|
||||
}
|
||||
|
||||
return cmdLine;
|
||||
}
|
||||
|
||||
std::string Process::getCwd()
|
||||
{
|
||||
if (cwd.empty()) {
|
||||
std::string cwdFile = getFile("cwd");
|
||||
char path[MAX_FILEPATH_LEN] = {};
|
||||
ssize_t len = readlink(cwdFile.c_str(), path, MAX_FILEPATH_LEN);
|
||||
if (len > 0 && len < MAX_FILEPATH_LEN) {
|
||||
cwd = std::string(path) + "/";
|
||||
}
|
||||
}
|
||||
|
||||
return cwd;
|
||||
}
|
||||
|
||||
std::string Process::getExe()
|
||||
{
|
||||
if (exe.empty()) {
|
||||
std::string cmdLineFile = getFile("exe");
|
||||
char path[MAX_FILEPATH_LEN] = {};
|
||||
ssize_t len = readlink(cmdLineFile.c_str(), path, MAX_FILEPATH_LEN);
|
||||
if (len > 0 && len < MAX_FILEPATH_LEN) {
|
||||
exe = std::string(path);
|
||||
}
|
||||
}
|
||||
|
||||
return exe;
|
||||
}
|
||||
|
||||
std::vector<std::string> Process::getEnviron()
|
||||
{
|
||||
if (environ.size() == 0) {
|
||||
std::string envFile = getFile("environ");
|
||||
environ = readFile(envFile);
|
||||
}
|
||||
|
||||
return environ;
|
||||
}
|
||||
|
||||
std::string Process::getEnv(const std::string &key)
|
||||
{
|
||||
if (environ.size() == 0)
|
||||
environ = getEnviron();
|
||||
|
||||
std::string keyPrefix = key + "=";
|
||||
for (auto & env : environ) {
|
||||
if (DString::startWith(env, keyPrefix)) {
|
||||
ulong len = keyPrefix.size();
|
||||
return env.substr(len, env.size() - len);
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
Status Process::getStatus()
|
||||
{
|
||||
if (status.size() == 0) {
|
||||
std::string statusFile = getFile("status");
|
||||
FILE *fp = fopen(statusFile.c_str(), "r");
|
||||
if (!fp)
|
||||
return status;
|
||||
|
||||
char line[MAX_LINE_LEN] = {0};
|
||||
while (fgets(line, MAX_LINE_LEN, fp)) {
|
||||
std::string info(line);
|
||||
std::vector<std::string> parts = DString::splitStr(info, ':');
|
||||
if (parts.size() == 2)
|
||||
status[parts[0]] = parts[1];
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
std::vector<int> Process::getUids()
|
||||
{
|
||||
if (uids.size() == 0) {
|
||||
if (status.find("Uid") != status.end()) {
|
||||
std::string uidGroup = status["Uid"];
|
||||
std::vector<std::string> parts = DString::splitStr(uidGroup, '\t');
|
||||
uids.reserve(parts.size());
|
||||
std::transform(parts.begin(), parts.end(), uids.begin(),
|
||||
[](std::string idStr) -> int {return std::stoi(idStr);});
|
||||
}
|
||||
}
|
||||
|
||||
return uids;
|
||||
}
|
||||
|
||||
int Process::getPid()
|
||||
{
|
||||
return pid;
|
||||
}
|
||||
|
||||
int Process::getPpid()
|
||||
{
|
||||
if (ppid == 0) {
|
||||
if (status.find("PPid") != status.end()) {
|
||||
ppid = std::stoi(status["PPid"]);
|
||||
}
|
||||
}
|
||||
|
||||
return ppid;
|
||||
}
|
||||
|
||||
std::string Process::getFile(const std::string &name)
|
||||
{
|
||||
return "/proc/" + std::to_string(pid) + "/" + name;
|
||||
}
|
||||
|
||||
// /proc is not real file system
|
||||
std::vector<std::string> Process::readFile(std::string fileName)
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
std::FILE *fp = std::fopen(fileName.c_str(), "r");
|
||||
if (!fp)
|
||||
return ret;
|
||||
|
||||
std::vector<char> content(FILECONTENLEN);
|
||||
std::size_t len = std::fread(&content[0], 1, FILECONTENLEN, fp);
|
||||
std::fclose(fp);
|
||||
|
||||
ret = DString::splitVectorChars(content, len, '\0');
|
||||
return ret;
|
||||
}
|
62
src/lib/process.h
Normal file
62
src/lib/process.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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/>.
|
||||
*/
|
||||
|
||||
#ifndef PROCESS_H
|
||||
#define PROCESS_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
typedef std::map<std::string, std::string> Status;
|
||||
|
||||
class Process
|
||||
{
|
||||
public:
|
||||
Process();
|
||||
explicit Process(int _pid);
|
||||
|
||||
bool isExist();
|
||||
std::vector<std::string> getCmdLine();
|
||||
std::string getCwd();
|
||||
std::string getExe();
|
||||
std::vector<std::string> getEnviron();
|
||||
std::string getEnv(const std::string &key);
|
||||
Status getStatus();
|
||||
std::vector<int> getUids();
|
||||
int getPid();
|
||||
int getPpid();
|
||||
|
||||
private:
|
||||
std::string getFile(const std::string &name);
|
||||
std::vector<std::string> readFile(std::string fileName);
|
||||
|
||||
int pid;
|
||||
std::vector<std::string> cmdLine;
|
||||
std::string cwd;
|
||||
std::string exe;
|
||||
std::vector<std::string> environ;
|
||||
Status status;
|
||||
std::vector<int> uids;
|
||||
int ppid;
|
||||
};
|
||||
|
||||
#endif // PROCESS_H
|
574
src/lib/xcbutils.cpp
Normal file
574
src/lib/xcbutils.cpp
Normal file
@ -0,0 +1,574 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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 "xcbutils.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
XCBUtils::XCBUtils()
|
||||
{
|
||||
connect = xcb_connect(nullptr, &screenNum); // nullptr表示默认使用环境变量$DISPLAY获取屏幕
|
||||
if (xcb_connection_has_error(connect)) {
|
||||
std::cout << "XCBUtils: init xcb_connect error" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!xcb_ewmh_init_atoms_replies(&ewmh,
|
||||
xcb_ewmh_init_atoms(connect, &ewmh), // 初始化Atom
|
||||
nullptr))
|
||||
std::cout << "XCBUtils: init ewmh error" << std::endl;
|
||||
}
|
||||
|
||||
XCBUtils::~XCBUtils()
|
||||
{
|
||||
if (connect) {
|
||||
xcb_disconnect(connect); // 关闭连接并释放
|
||||
connect = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
XWindow XCBUtils::allocId()
|
||||
{
|
||||
return xcb_generate_id(connect);
|
||||
}
|
||||
|
||||
void XCBUtils::killClientChecked(XWindow xid)
|
||||
{
|
||||
xcb_kill_client_checked(connect, xid);
|
||||
}
|
||||
|
||||
xcb_get_property_reply_t *XCBUtils::getPropertyValueReply(XWindow xid, XCBAtom property, XCBAtom type)
|
||||
{
|
||||
xcb_get_property_cookie_t cookie = xcb_get_property(connect,
|
||||
0,
|
||||
xid,
|
||||
property,
|
||||
type,
|
||||
0,
|
||||
MAXLEN);
|
||||
return xcb_get_property_reply(connect, cookie, nullptr);
|
||||
}
|
||||
|
||||
void *XCBUtils::getPropertyValue(XWindow xid, XCBAtom property, XCBAtom type)
|
||||
{
|
||||
void *value = nullptr;
|
||||
xcb_get_property_reply_t *reply = getPropertyValueReply(xid, property, type);
|
||||
if (reply) {
|
||||
if (xcb_get_property_value_length(reply) > 0) {
|
||||
value = xcb_get_property_value(reply);
|
||||
}
|
||||
free(reply);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string XCBUtils::getUTF8PropertyStr(XWindow xid, XCBAtom property)
|
||||
{
|
||||
std::string ret;
|
||||
xcb_get_property_reply_t *reply = getPropertyValueReply(xid, property, ewmh.UTF8_STRING);
|
||||
if (reply) {
|
||||
ret = getUTF8StrFromReply(reply);
|
||||
|
||||
free(reply);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
XCBAtom XCBUtils::getAtom(const char *name)
|
||||
{
|
||||
XCBAtom ret = atomCache.getVal(name);
|
||||
if (ret == ATOMNONE) {
|
||||
xcb_intern_atom_cookie_t cookie = xcb_intern_atom(connect, false, strlen(name), name);
|
||||
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply (connect,
|
||||
cookie,
|
||||
nullptr);
|
||||
if (reply) {
|
||||
atomCache.store(name, reply->atom);
|
||||
ret = reply->atom;
|
||||
|
||||
free(reply);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string XCBUtils::getAtomName(XCBAtom atom)
|
||||
{
|
||||
std::string ret = atomCache.getName(atom);
|
||||
if (ret.empty()) {
|
||||
xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name(connect, atom);
|
||||
xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(connect,
|
||||
cookie,
|
||||
nullptr);
|
||||
if (reply) {
|
||||
char *name = xcb_get_atom_name_name(reply);
|
||||
if (name) {
|
||||
atomCache.store(name, atom);
|
||||
ret = name;
|
||||
}
|
||||
|
||||
free(reply);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Geometry XCBUtils::getWindowGeometry(XWindow xid)
|
||||
{
|
||||
Geometry ret;
|
||||
xcb_get_geometry_cookie_t cookie = xcb_get_geometry(connect, xid);
|
||||
xcb_get_geometry_reply_t *reply = xcb_get_geometry_reply(connect, cookie, nullptr);
|
||||
if (reply) {
|
||||
ret.x = reply->x;
|
||||
ret.y = reply->y;
|
||||
ret.width = reply->width;
|
||||
ret.height = reply->height;
|
||||
|
||||
free(reply);
|
||||
} else {
|
||||
std::cout << xid << " getWindowGeometry err" << std::endl;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
XWindow XCBUtils::getActiveWindow()
|
||||
{
|
||||
XWindow ret;
|
||||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_active_window(&ewmh, screenNum);
|
||||
if (!xcb_ewmh_get_active_window_reply(&ewmh, cookie, &ret, nullptr))
|
||||
std::cout << "getActiveWindow error" << std::endl;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void XCBUtils::setActiveWindow(XWindow xid)
|
||||
{
|
||||
xcb_ewmh_set_active_window(&ewmh, screenNum, xid);
|
||||
}
|
||||
|
||||
std::list<XWindow> XCBUtils::getClientList()
|
||||
{
|
||||
std::list<XWindow> ret;
|
||||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_client_list(&ewmh, screenNum);
|
||||
xcb_ewmh_get_windows_reply_t reply;
|
||||
if (!xcb_ewmh_get_client_list_reply(&ewmh, cookie, &reply, nullptr))
|
||||
std::cout << "getClientList error" << std::endl;
|
||||
|
||||
for (uint32_t i = 0; i < reply.windows_len; i++)
|
||||
ret.push_back(reply.windows[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::list<XWindow> XCBUtils::getClientListStacking()
|
||||
{
|
||||
std::list<XWindow> ret;
|
||||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_client_list_stacking(&ewmh, screenNum);
|
||||
xcb_ewmh_get_windows_reply_t reply;
|
||||
if (!xcb_ewmh_get_client_list_stacking_reply(&ewmh, cookie, &reply, nullptr))
|
||||
std::cout << "getClientListStacking error" << std::endl;
|
||||
|
||||
for (uint32_t i = 0; i < reply.windows_len; i++)
|
||||
ret.push_back(reply.windows[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<XCBAtom> XCBUtils::getWMState(XWindow xid)
|
||||
{
|
||||
std::vector<XCBAtom> ret;
|
||||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_state(&ewmh, xid);
|
||||
xcb_ewmh_get_atoms_reply_t reply; // a list of Atom
|
||||
if (xcb_ewmh_get_wm_state_reply(&ewmh, cookie, &reply, nullptr)) {
|
||||
for (uint32_t i = 0; i < reply.atoms_len; i++) {
|
||||
ret.push_back(reply.atoms[i]);
|
||||
}
|
||||
} else {
|
||||
std::cout << xid << " getWMState error" << std::endl;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<XCBAtom> XCBUtils::getWMWindoType(XWindow xid)
|
||||
{
|
||||
std::vector<XCBAtom> ret;
|
||||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_window_type(&ewmh, xid);
|
||||
xcb_ewmh_get_atoms_reply_t reply; // a list of Atom
|
||||
if (!xcb_ewmh_get_wm_window_type_reply(&ewmh, cookie, &reply, nullptr))
|
||||
std::cout << xid << " getWMWindoType error" << std::endl;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<XCBAtom> XCBUtils::getWMAllowedActions(XWindow xid)
|
||||
{
|
||||
std::vector<XCBAtom> ret;
|
||||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_allowed_actions(&ewmh, xid);
|
||||
xcb_ewmh_get_atoms_reply_t reply; // a list of Atoms
|
||||
if (!xcb_ewmh_get_wm_allowed_actions_reply(&ewmh, cookie, &reply, nullptr))
|
||||
std::cout << xid << " getWMAllowedActions error" << std::endl;
|
||||
|
||||
for (uint32_t i = 0; i < reply.atoms_len; i++) {
|
||||
ret.push_back(reply.atoms[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void XCBUtils::setWMAllowedActions(XWindow xid, std::vector<XCBAtom> actions)
|
||||
{
|
||||
XCBAtom list[MAXALLOWEDACTIONLEN] {0};
|
||||
for (size_t i = 0; i < actions.size(); i++)
|
||||
list[i] = actions[i];
|
||||
|
||||
xcb_ewmh_set_wm_allowed_actions(&ewmh, xid, actions.size(), list);
|
||||
}
|
||||
|
||||
std::string XCBUtils::getWMName(XWindow xid)
|
||||
{
|
||||
std::string ret;
|
||||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_name(&ewmh, xid);
|
||||
xcb_ewmh_get_utf8_strings_reply_t reply1;
|
||||
if (!xcb_ewmh_get_wm_name_reply(&ewmh, cookie, &reply1, nullptr))
|
||||
std::cout << xid << " getWMName error" << std::endl;
|
||||
|
||||
ret.assign(reply1.strings);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t XCBUtils::getWMPid(XWindow xid)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_pid(&ewmh, xid);
|
||||
if (!xcb_ewmh_get_wm_pid_reply(&ewmh, cookie, &ret, nullptr))
|
||||
std::cout << xid << " getWMPid error" << std::endl;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string XCBUtils::getWMIconName(XWindow xid)
|
||||
{
|
||||
std::string ret;
|
||||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_icon_name(&ewmh, xid);
|
||||
xcb_ewmh_get_utf8_strings_reply_t reply;
|
||||
if (!xcb_ewmh_get_wm_icon_name_reply(&ewmh, cookie, &reply, nullptr))
|
||||
std::cout << xid << " getWMIconName error" << std::endl;
|
||||
|
||||
ret.assign(reply.strings);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<WMIcon> XCBUtils::getWMIcon(XWindow xid)
|
||||
{
|
||||
std::vector<WMIcon> ret;
|
||||
/*
|
||||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_icon(&ewmh, xid);
|
||||
xcb_ewmh_get_wm_icon_reply_t reply;
|
||||
if (xcb_ewmh_get_wm_icon_reply(&ewmh, cookie, &reply, nullptr)) {
|
||||
xcb_ewmh_wm_icon_iterator_t iter = xcb_ewmh_get_wm_icon_iterator(&reply);
|
||||
auto fcn = [](xcb_ewmh_wm_icon_iterator_t it) {
|
||||
std::vector<BYTE> data;
|
||||
uint32_t *dat = it.data;
|
||||
int area = it.width * it.height;
|
||||
for (int i = 0; i < (2 + area) * 4; i++, dat++) { // TODO check data accuracy
|
||||
data.push_back(*dat);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
ret.push_back({iter.width, iter.height, fcn(iter)});
|
||||
|
||||
while (iter.rem >= 1) {
|
||||
xcb_ewmh_get_wm_icon_next(&iter);
|
||||
ret.push_back({iter.width, iter.height, fcn(iter)});
|
||||
}
|
||||
|
||||
xcb_ewmh_get_wm_icon_reply_wipe(&reply); // clear
|
||||
}
|
||||
*/
|
||||
return ret;
|
||||
}
|
||||
|
||||
XWindow XCBUtils::getWMClientLeader(XWindow xid)
|
||||
{
|
||||
XWindow ret;
|
||||
XCBAtom atom = getAtom("WM_CLIENT_LEADER");
|
||||
void *value = getPropertyValue(xid, atom, XCB_ATOM_INTEGER);
|
||||
std::cout << "getWMClientLeader:" << (char*)value << std::endl;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void XCBUtils::requestCloseWindow(XWindow xid, uint32_t timestamp)
|
||||
{
|
||||
xcb_ewmh_request_close_window(&ewmh, screenNum, xid, timestamp, XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER);
|
||||
}
|
||||
|
||||
uint32_t XCBUtils::getWMDesktop(XWindow xid)
|
||||
{
|
||||
uint32_t ret;
|
||||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_desktop(&ewmh, xid);
|
||||
if (!xcb_ewmh_get_wm_desktop_reply(&ewmh, cookie, &ret, nullptr))
|
||||
std::cout << xid << " getWMDesktop error" << std::endl;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void XCBUtils::setWMDesktop(XWindow xid, uint32_t desktop)
|
||||
{
|
||||
xcb_ewmh_set_wm_desktop(&ewmh, xid, desktop);
|
||||
}
|
||||
|
||||
void XCBUtils::setCurrentWMDesktop(uint32_t desktop)
|
||||
{
|
||||
xcb_ewmh_set_current_desktop(&ewmh, screenNum, desktop);
|
||||
}
|
||||
|
||||
uint32_t XCBUtils::getCurrentWMDesktop()
|
||||
{
|
||||
uint32_t ret;
|
||||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_current_desktop(&ewmh, screenNum);
|
||||
if (!xcb_ewmh_get_current_desktop_reply(&ewmh, cookie, &ret, nullptr))
|
||||
std::cout << "getCurrentWMDesktop error" << std::endl;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool XCBUtils::isGoodWindow(XWindow xid)
|
||||
{
|
||||
bool ret = false;
|
||||
xcb_get_geometry_cookie_t cookie = xcb_get_geometry(connect, xid);
|
||||
xcb_generic_error_t **errStore = nullptr;
|
||||
xcb_get_geometry_reply_t *reply = xcb_get_geometry_reply(connect, cookie, errStore);
|
||||
if (reply) {
|
||||
if (!errStore) // 正常获取窗口geometry则判定为good
|
||||
ret = true;
|
||||
|
||||
free(reply);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TODO XCB下无_MOTIF_WM_HINTS属性
|
||||
MotifWMHints XCBUtils::getWindowMotifWMHints(XWindow xid)
|
||||
{
|
||||
MotifWMHints ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool XCBUtils::hasXEmbedInfo(XWindow xid)
|
||||
{
|
||||
//XCBAtom atom = getAtom("_XEMBED_INFO");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
XWindow XCBUtils::getWMTransientFor(XWindow xid)
|
||||
{
|
||||
XWindow ret;
|
||||
xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_transient_for(connect, xid);
|
||||
if (!xcb_icccm_get_wm_transient_for_reply(connect, cookie, &ret, nullptr))
|
||||
std::cout << xid << " getWMTransientFor error" << std::endl;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t XCBUtils::getWMUserTime(XWindow xid)
|
||||
{
|
||||
uint32_t ret;
|
||||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_user_time(&ewmh, xid);
|
||||
if (!xcb_ewmh_get_wm_user_time_reply(&ewmh, cookie, &ret, nullptr))
|
||||
std::cout << xid << " getWMUserTime error" << std::endl;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int XCBUtils::getWMUserTimeWindow(XWindow xid)
|
||||
{
|
||||
XCBAtom ret;
|
||||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_user_time_window(&ewmh, xid);
|
||||
if (!xcb_ewmh_get_wm_user_time_window_reply(&ewmh, cookie, &ret, NULL))
|
||||
std::cout << xid << " getWMUserTimeWindow error" << std::endl;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
WMClass XCBUtils::getWMClass(XWindow xid)
|
||||
{
|
||||
WMClass ret;
|
||||
xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_class(connect, xid);
|
||||
xcb_icccm_get_wm_class_reply_t reply;
|
||||
if (!xcb_icccm_get_wm_class_reply(connect, cookie, &reply, nullptr)) {
|
||||
if (reply.class_name)
|
||||
ret.className.assign(reply.class_name);
|
||||
|
||||
if (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;
|
||||
}
|
||||
|
||||
// TODO
|
||||
void XCBUtils::minimizeWindow(XWindow xid)
|
||||
{
|
||||
xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_hints(connect, xid);
|
||||
xcb_icccm_wm_hints_t *hints = new xcb_icccm_wm_hints_t; // 分配堆空间
|
||||
xcb_icccm_get_wm_hints_reply(connect, cookie, hints, nullptr);
|
||||
xcb_icccm_wm_hints_set_iconic(hints);
|
||||
xcb_icccm_set_wm_hints(connect, xid, hints);
|
||||
free(hints);
|
||||
}
|
||||
|
||||
void XCBUtils::maxmizeWindow(XWindow xid)
|
||||
{
|
||||
xcb_ewmh_request_change_wm_state(&ewmh
|
||||
, screenNum
|
||||
, xid
|
||||
, XCB_EWMH_WM_STATE_ADD
|
||||
, getAtom("_NET_WM_STATE_MAXIMIZED_VERT")
|
||||
, getAtom("_NET_WM_STATE_MAXIMIZED_HORZ")
|
||||
, XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER);
|
||||
}
|
||||
|
||||
// TODO
|
||||
std::vector<std::string> XCBUtils::getWMCommand(XWindow xid)
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
xcb_get_property_reply_t *reply = getPropertyValueReply(xid, XCB_ATOM_WM_COMMAND, ewmh.UTF8_STRING);
|
||||
if (reply) {
|
||||
ret = getUTF8StrsFromReply(reply);
|
||||
free(reply);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string XCBUtils::getUTF8StrFromReply(xcb_get_property_reply_t *reply)
|
||||
{
|
||||
std::string ret;
|
||||
if (!reply || reply->format != 8)
|
||||
return ret;
|
||||
|
||||
char data[12] = {0};
|
||||
for (uint32_t i=0; i < reply->value_len; i++) {
|
||||
data[i] = char(reply->pad0[i]);
|
||||
}
|
||||
ret.assign(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::string> XCBUtils::getUTF8StrsFromReply(xcb_get_property_reply_t *reply)
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
if (!reply)
|
||||
return ret;
|
||||
|
||||
if (reply->format != 8)
|
||||
return ret;
|
||||
|
||||
|
||||
// 字符串拆分
|
||||
uint32_t start = 0;
|
||||
for (uint32_t i=0; i < reply->value_len; i++) {
|
||||
if (reply->pad0[i] == 0) {
|
||||
char data[12] = {0};
|
||||
int count = 0;
|
||||
for (uint32_t j=start; j < i; j++)
|
||||
data[count++] = char(reply->pad0[j]);
|
||||
|
||||
data[count] = 0;
|
||||
ret.push_back(data);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
XWindow XCBUtils::getRootWindow()
|
||||
{
|
||||
XWindow rootWindow = 0;
|
||||
/* Get the first screen */
|
||||
xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(connect)).data;
|
||||
if (screen)
|
||||
rootWindow = screen->root;
|
||||
|
||||
std::cout << "getRootWinodw: " << rootWindow << std::endl;
|
||||
return rootWindow;
|
||||
}
|
||||
|
||||
void XCBUtils::registerEvents(XWindow xid, uint32_t eventMask)
|
||||
{
|
||||
uint32_t value[1] = {eventMask};
|
||||
xcb_void_cookie_t cookie = xcb_change_window_attributes_checked(connect,
|
||||
xid,
|
||||
XCB_CW_EVENT_MASK,
|
||||
&value);
|
||||
xcb_flush(connect);
|
||||
|
||||
xcb_generic_error_t *error = xcb_request_check(connect, cookie);
|
||||
if (error != nullptr) {
|
||||
std::cout << "window " << xid << "registerEvents error" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AtomCache::AtomCache()
|
||||
{
|
||||
}
|
||||
|
||||
XCBAtom AtomCache::getVal(std::string name)
|
||||
{
|
||||
XCBAtom atom = ATOMNONE;
|
||||
auto search = atoms.find(name);
|
||||
if (search != atoms.end())
|
||||
atom = search->second;
|
||||
|
||||
return atom;
|
||||
}
|
||||
|
||||
std::string AtomCache::getName(XCBAtom atom)
|
||||
{
|
||||
std::string ret;
|
||||
auto search = atomNames.find(atom);
|
||||
if (search != atomNames.end())
|
||||
ret = search->second;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AtomCache::store(std::string name, XCBAtom value)
|
||||
{
|
||||
atoms[name] = value;
|
||||
atomNames[value] = name;
|
||||
}
|
272
src/lib/xcbutils.h
Normal file
272
src/lib/xcbutils.h
Normal file
@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ~ 2023 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/>.
|
||||
*/
|
||||
|
||||
#ifndef XCBUTILS_H
|
||||
#define XCBUTILS_H
|
||||
|
||||
#include <xcb/xproto.h>
|
||||
#include <xcb/xcb_ewmh.h>
|
||||
#include <xcb/xcb_icccm.h>
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#define MAXLEN 0xffff
|
||||
#define MAXALLOWEDACTIONLEN 256
|
||||
#define ATOMNONE 0
|
||||
|
||||
typedef xcb_window_t XWindow ;
|
||||
typedef xcb_atom_t XCBAtom;
|
||||
typedef xcb_destroy_notify_event_t DestroyEvent;
|
||||
typedef xcb_map_notify_event_t MapEvent;
|
||||
typedef xcb_configure_notify_event_t ConfigureEvent;
|
||||
typedef xcb_property_notify_event_t PropertyEvent;
|
||||
typedef xcb_event_mask_t EventMask;
|
||||
typedef struct {
|
||||
std::string instanceName;
|
||||
std::string className;
|
||||
} WMClass;
|
||||
|
||||
typedef struct {
|
||||
int16_t x, y;
|
||||
uint16_t width, height;
|
||||
} Geometry;
|
||||
|
||||
typedef struct {
|
||||
uint32_t flags;
|
||||
uint32_t functions;
|
||||
uint32_t decorations;
|
||||
int32_t inputMode;
|
||||
uint32_t status;
|
||||
} MotifWMHints;
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef struct {
|
||||
uint32_t width; /** Icon width */
|
||||
uint32_t height; /** Icon height */
|
||||
std::vector<BYTE> data; /** Rows, left to right and top to bottom of the CARDINAL ARGB */
|
||||
} WMIcon;
|
||||
|
||||
// 缓存atom,减少X访问 TODO 加读写锁
|
||||
class AtomCache {
|
||||
public:
|
||||
AtomCache();
|
||||
|
||||
XCBAtom getVal(std::string name);
|
||||
std::string getName(XCBAtom atom);
|
||||
void store(std::string name, XCBAtom value);
|
||||
|
||||
public:
|
||||
std::map<std::string, XCBAtom> atoms;
|
||||
std::map<XCBAtom, std::string> atomNames;
|
||||
};
|
||||
|
||||
// XCB接口封装, 参考getCurrentWMDesktop
|
||||
class XCBUtils
|
||||
{
|
||||
XCBUtils();
|
||||
XCBUtils(const XCBUtils &other);
|
||||
XCBUtils & operator= (const XCBUtils &other);
|
||||
~XCBUtils();
|
||||
|
||||
public:
|
||||
static XCBUtils *instance() {
|
||||
static XCBUtils instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
// test
|
||||
xcb_connection_t *getConnect() {return connect;}
|
||||
|
||||
/************************* xcb method ***************************/
|
||||
// 分配XID
|
||||
XWindow allocId();
|
||||
|
||||
/************************* xpropto method ***************************/
|
||||
// 杀掉进程
|
||||
void killClientChecked(XWindow xid);
|
||||
|
||||
// 获取属性reply, 返回值必须free
|
||||
xcb_get_property_reply_t *getPropertyValueReply(XWindow xid, XCBAtom property, XCBAtom type = XCB_ATOM_ATOM);
|
||||
|
||||
// 获取属性
|
||||
void *getPropertyValue(XWindow xid, XCBAtom property, XCBAtom type = XCB_ATOM_ATOM);
|
||||
|
||||
// 获取字符串属性
|
||||
std::string getUTF8PropertyStr(XWindow xid, XCBAtom property);
|
||||
|
||||
// 获取名称对应的Atom
|
||||
XCBAtom getAtom(const char *name);
|
||||
|
||||
// 获取Atom对应的名称
|
||||
std::string getAtomName(XCBAtom atom);
|
||||
|
||||
// 获取窗口矩形
|
||||
Geometry getWindowGeometry(XWindow xid);
|
||||
|
||||
// 判断当前窗口是否正常
|
||||
bool isGoodWindow(XWindow xid);
|
||||
|
||||
// 获取窗口
|
||||
MotifWMHints getWindowMotifWMHints(XWindow xid);
|
||||
|
||||
bool hasXEmbedInfo(XWindow xid);
|
||||
|
||||
/************************* ewmh method ***************************/
|
||||
|
||||
// 获取活动窗口 _NET_ACTIVE_WINDOW
|
||||
XWindow getActiveWindow();
|
||||
|
||||
// 设置活动窗口 _NET_ACTIVE_WINDOW
|
||||
void setActiveWindow(XWindow xid);
|
||||
|
||||
// 获取窗口列表 _NET_CLIENT_LIST
|
||||
std::list<XWindow> getClientList();
|
||||
|
||||
// 获取窗口列表 _NET_CLIENT_LIST_STACKING
|
||||
std::list<XWindow> getClientListStacking();
|
||||
|
||||
// 获取窗口状态 _NET_WM_STATE
|
||||
/*
|
||||
_NET_WM_STATE_MODAL, ATOM
|
||||
_NET_WM_STATE_STICKY, ATOM
|
||||
_NET_WM_STATE_MAXIMIZED_VERT, ATOM
|
||||
_NET_WM_STATE_MAXIMIZED_HORZ, ATOM
|
||||
_NET_WM_STATE_SHADED, ATOM
|
||||
_NET_WM_STATE_SKIP_TASKBAR, ATOM
|
||||
_NET_WM_STATE_SKIP_PAGER, ATOM
|
||||
_NET_WM_STATE_HIDDEN, ATOM
|
||||
_NET_WM_STATE_FULLSCREEN, ATOM
|
||||
_NET_WM_STATE_ABOVE, ATOM
|
||||
_NET_WM_STATE_BELOW, ATOM
|
||||
_NET_WM_STATE_DEMANDS_ATTENTION, ATOM
|
||||
*/
|
||||
std::vector<XCBAtom> getWMState(XWindow xid);
|
||||
|
||||
// 获取窗口类型 _NET_WM_WINDOW_TYPE
|
||||
// Rationale: This hint is intended to replace the MOTIF hints.
|
||||
// One of the objections to the MOTIF hints is that they are a purely visual description of the window decoration.
|
||||
// By describing the function of the window, the Window Manager can apply consistent decoration and behavior to windows of the same type.
|
||||
// Possible examples of behavior include keeping dock/panels on top or allowing pinnable menus / toolbars to only be hidden
|
||||
// when another window has focus
|
||||
/*
|
||||
_NET_WM_WINDOW_TYPE_DESKTOP, ATOM
|
||||
_NET_WM_WINDOW_TYPE_DOCK, ATOM
|
||||
_NET_WM_WINDOW_TYPE_TOOLBAR, ATOM
|
||||
_NET_WM_WINDOW_TYPE_MENU, ATOM
|
||||
_NET_WM_WINDOW_TYPE_UTILITY, ATOM
|
||||
_NET_WM_WINDOW_TYPE_SPLASH, ATOM
|
||||
_NET_WM_WINDOW_TYPE_DIALOG, ATOM
|
||||
_NET_WM_WINDOW_TYPE_DROPDOWN_MENU, ATOM
|
||||
_NET_WM_WINDOW_TYPE_POPUP_MENU, ATOM
|
||||
_NET_WM_WINDOW_TYPE_TOOLTIP, ATOM
|
||||
_NET_WM_WINDOW_TYPE_NOTIFICATION, ATOM
|
||||
_NET_WM_WINDOW_TYPE_COMBO, ATOM
|
||||
_NET_WM_WINDOW_TYPE_DND, ATOM
|
||||
_NET_WM_WINDOW_TYPE_NORMAL, ATOM
|
||||
* */
|
||||
std::vector<XCBAtom> getWMWindoType(XWindow xid);
|
||||
|
||||
// 获取窗口许可动作 _NET_WM_ALLOWED_ACTIONS
|
||||
std::vector<XCBAtom> getWMAllowedActions(XWindow xid);
|
||||
|
||||
// 设置窗口许可动作
|
||||
void setWMAllowedActions(XWindow xid, std::vector<XCBAtom> actions);
|
||||
|
||||
// 获取窗口名称 _NET_WM_NAME
|
||||
std::string getWMName(XWindow xid);
|
||||
|
||||
// 获取窗口所属进程 _NET_WM_PID
|
||||
uint32_t getWMPid(XWindow xid);
|
||||
|
||||
// 获取窗口图标 _NET_WM_ICON_NAME
|
||||
std::string getWMIconName(XWindow xid);
|
||||
|
||||
// _NET_WM_ICON
|
||||
std::vector<WMIcon> getWMIcon(XWindow xid);
|
||||
|
||||
// WM_CLIENT_LEADER
|
||||
XWindow getWMClientLeader(XWindow xid);
|
||||
|
||||
// 关闭窗口 _NET_CLOSE_WINDOW
|
||||
void requestCloseWindow(XWindow xid, uint32_t timestamp);
|
||||
|
||||
// 获取窗口对应桌面 _NET_WM_DESKTOP
|
||||
uint32_t getWMDesktop(XWindow xid);
|
||||
|
||||
// 设置窗口当前桌面
|
||||
void setWMDesktop(XWindow xid, uint32_t desktop);
|
||||
|
||||
// 设置桌面
|
||||
void setCurrentWMDesktop(uint32_t desktop);
|
||||
|
||||
// 获取当前桌面 _NET_CURRENT_DESKTOP
|
||||
uint32_t getCurrentWMDesktop();
|
||||
|
||||
|
||||
/************************* icccm method ***************************/
|
||||
// The WM_TRANSIENT_FOR hint of the ICCCM allows clients to specify that a toplevel window may be closed before the client finishes.
|
||||
// A typical example of a transient window is a dialog.
|
||||
// Some dialogs can be open for a long time, while the user continues to work in the main window.
|
||||
// Other dialogs have to be closed before the user can continue to work in the main window
|
||||
XWindow getWMTransientFor(XWindow xid);
|
||||
|
||||
uint32_t getWMUserTime(XWindow xid);
|
||||
|
||||
int getWMUserTimeWindow(XWindow xid);
|
||||
|
||||
// 获取窗口类型
|
||||
WMClass getWMClass(XWindow xid);
|
||||
|
||||
// 最小化窗口
|
||||
void minimizeWindow(XWindow xid);
|
||||
|
||||
// 最大化窗口
|
||||
void maxmizeWindow(XWindow xid);
|
||||
|
||||
/************************* other method ***************************/
|
||||
// 获取窗口command
|
||||
std::vector<std::string> getWMCommand(XWindow xid);
|
||||
|
||||
// 解析属性为UTF8格式字符串
|
||||
std::string getUTF8StrFromReply(xcb_get_property_reply_t *reply);
|
||||
|
||||
// 解析属性为UTF8格式字符串字符数组
|
||||
std::vector<std::string> getUTF8StrsFromReply(xcb_get_property_reply_t *reply);
|
||||
|
||||
// 获取根窗口
|
||||
XWindow getRootWindow();
|
||||
|
||||
// 注册事件
|
||||
void registerEvents(XWindow xid, uint32_t eventMask);
|
||||
|
||||
|
||||
private:
|
||||
xcb_connection_t *connect;
|
||||
int screenNum;
|
||||
|
||||
xcb_ewmh_connection_t ewmh;
|
||||
AtomCache atomCache; // 和ewmh中Atom类型存在重复部分,扩张了自定义类型
|
||||
};
|
||||
|
||||
#endif // XCBUTILS_H
|
Reference in New Issue
Block a user