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:
Li Xi
2022-04-24 14:52:13 +08:00
committed by weizhixiang
parent dd7d4737bf
commit 13a1cabda1
118 changed files with 18347 additions and 3 deletions

169
src/lib/basedir.cpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 &section, 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 &section, const std::string &key, bool defaultValue)
{
std::vector<bool> tmp;
return tmp;
}
int KeyFile::getInt(const std::string &section, 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 &section, const std::string &key, int defaultValue)
{
std::vector<int> tmp;
return tmp;
}
// TODO
int64_t KeyFile::getInt64(const std::string &section, const std::string &key, int64_t defaultValue)
{
return int64_t(0);
}
// TODO
float KeyFile::getFloat(const std::string &section, const std::string &key, float defaultValue)
{
return 1.0;
}
std::string KeyFile::getStr(const std::string &section, 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 &section, 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 &section, 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 &section, const std::string &key)
{
std::string value = getStr(section, key);
return DString::splitStr(value, listSeparator);
}
std::vector<std::string> KeyFile::getLocaleStrList(const std::string &section, 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 &section, 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
View 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 &section, const std::string &key, bool defaultValue = false);
std::vector<bool> getBoolList(const std::string &section, const std::string &key, bool defaultValue = false);
int getInt(const std::string &section, const std::string &key, int defaultValue = 0);
std::vector<int> getIntList(const std::string &section, const std::string &key, int defaultValue = 0);
int64_t getInt64(const std::string &section, const std::string &key, int64_t defaultValue = 0);
float getFloat(const std::string &section, const std::string &key, float defaultValue = 0);
std::string getStr(const std::string &section, const std::string &key, std::string defaultValue = "");
bool containKey(const std::string &section, const std::string &key);
std::string getLocaleStr(const std::string &section, const std::string &key, std::string defaultLocale = "");
std::vector<std::string> getStrList(const std::string &section, const std::string &key);
std::vector<std::string> getLocaleStrList(const std::string &section, const std::string &key, std::string defaultLocale = "");
void setKey(const std::string &section, 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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