mirror of
https://github.com/linuxdeepin/dde-dock.git
synced 2025-05-30 22:01:41 +00:00

1. taskmanager used to identify which entry should map to window in x11 environmrnt, listen to xevent in anohter thread, and handle those event when window create, destory, changed. use some way to identify which entry(desktopfile) should mapped to changed window. in wayland, connected plsamawindow signal(window created destoried. 2. use taskmanager instead of dbus in old dock code log: as title
761 lines
22 KiB
C++
761 lines
22 KiB
C++
// SPDX-FileCopyrightText: 2018 - 2023 UnionTech Software Technology Co., Ltd.
|
||
//
|
||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||
|
||
#include "xcbutils.h"
|
||
|
||
#include <cstdint>
|
||
#include <utility>
|
||
#include <iostream>
|
||
#include <cstring>
|
||
#include <memory>
|
||
#include <algorithm>
|
||
|
||
#include <X11/Xlib.h>
|
||
#include <X11/extensions/XRes.h>
|
||
|
||
XCBUtils::XCBUtils()
|
||
{
|
||
m_connect = xcb_connect(nullptr, &m_screenNum); // nullptr表示默认使用环境变量$DISPLAY获取屏幕
|
||
if (xcb_connection_has_error(m_connect)) {
|
||
std::cout << "XCBUtils: init xcb_connect error" << std::endl;
|
||
return;
|
||
}
|
||
|
||
if (!xcb_ewmh_init_atoms_replies(&m_ewmh,
|
||
xcb_ewmh_init_atoms(m_connect, &m_ewmh), // 初始化Atom
|
||
nullptr))
|
||
std::cout << "XCBUtils: init ewmh error" << std::endl;
|
||
}
|
||
|
||
XCBUtils::~XCBUtils()
|
||
{
|
||
if (m_connect) {
|
||
xcb_disconnect(m_connect); // 关闭连接并释放
|
||
m_connect = nullptr;
|
||
}
|
||
}
|
||
|
||
XWindow XCBUtils::allocId()
|
||
{
|
||
return xcb_generate_id(m_connect);
|
||
}
|
||
|
||
void XCBUtils::flush()
|
||
{
|
||
xcb_flush(m_connect);
|
||
}
|
||
|
||
void XCBUtils::killClientChecked(XWindow xid)
|
||
{
|
||
xcb_kill_client_checked(m_connect, xid);
|
||
}
|
||
|
||
xcb_get_property_reply_t *XCBUtils::getPropertyValueReply(XWindow xid, XCBAtom property, XCBAtom type)
|
||
{
|
||
xcb_get_property_cookie_t cookie = xcb_get_property(m_connect,
|
||
0,
|
||
xid,
|
||
property,
|
||
type,
|
||
0,
|
||
MAXLEN);
|
||
return xcb_get_property_reply(m_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, m_ewmh.UTF8_STRING);
|
||
if (reply) {
|
||
ret = getUTF8StrFromReply(reply);
|
||
free(reply);
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
XCBAtom XCBUtils::getAtom(const char *name)
|
||
{
|
||
XCBAtom ret = m_atomCache.getVal(name);
|
||
if (ret == ATOMNONE) {
|
||
xcb_intern_atom_cookie_t cookie = xcb_intern_atom(m_connect, false, strlen(name), name);
|
||
std::shared_ptr<xcb_intern_atom_reply_t> reply(xcb_intern_atom_reply(m_connect, cookie, nullptr), [=](xcb_intern_atom_reply_t* reply){
|
||
free(reply);}
|
||
);
|
||
if (reply) {
|
||
m_atomCache.store(name, xcb_atom_t(reply->atom));
|
||
ret = reply->atom;
|
||
}
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
std::string XCBUtils::getAtomName(XCBAtom atom)
|
||
{
|
||
std::string ret = m_atomCache.getName(atom);
|
||
if (ret.empty()) {
|
||
xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name(m_connect, atom);
|
||
std::shared_ptr<xcb_get_atom_name_reply_t> reply(
|
||
xcb_get_atom_name_reply(m_connect, cookie, nullptr),
|
||
[=](xcb_get_atom_name_reply_t* reply) {free(reply);});
|
||
if (reply) {
|
||
char *name = xcb_get_atom_name_name(reply.get());
|
||
if (name) {
|
||
m_atomCache.store(name, atom);
|
||
ret = name;
|
||
}
|
||
}
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
Geometry XCBUtils::getWindowGeometry(XWindow xid)
|
||
{
|
||
xcb_get_geometry_cookie_t cookie = xcb_get_geometry(m_connect, xcb_drawable_t(xid));
|
||
std::shared_ptr<xcb_get_geometry_reply_t> reply(
|
||
xcb_get_geometry_reply(m_connect, cookie, nullptr),
|
||
[=](xcb_get_geometry_reply_t* reply){free(reply);}
|
||
);
|
||
if (!reply) {
|
||
std::cout << xid << " getWindowGeometry err" << std::endl;
|
||
return Geometry();
|
||
}
|
||
|
||
Geometry ret;
|
||
ret.x = reply->x;
|
||
ret.y = reply->y;
|
||
ret.width = reply->width;
|
||
ret.height = reply->height;
|
||
|
||
const xcb_setup_t *xcbSetup = xcb_get_setup(m_connect);
|
||
if (!xcbSetup)
|
||
return Geometry();
|
||
|
||
xcb_screen_iterator_t xcbScreenIterator = xcb_setup_roots_iterator(xcbSetup);
|
||
std::shared_ptr<xcb_translate_coordinates_reply_t> translateReply(
|
||
xcb_translate_coordinates_reply(m_connect,
|
||
xcb_translate_coordinates(m_connect, xid, xcbScreenIterator.data->root, 0, 0),
|
||
nullptr),
|
||
[=](xcb_translate_coordinates_reply_t* translateReply){free(translateReply);});
|
||
|
||
if (translateReply) {
|
||
ret.x = translateReply->dst_x;
|
||
ret.y = translateReply->dst_y;
|
||
}
|
||
|
||
XWindow dWin = getDecorativeWindow(xid);
|
||
reply.reset(xcb_get_geometry_reply(m_connect, xcb_get_geometry(m_connect, xcb_drawable_t(dWin)), nullptr),
|
||
[=](xcb_get_geometry_reply_t* reply){free(reply);});
|
||
if (!reply)
|
||
return ret;
|
||
|
||
if (reply->x == ret.x && reply->y == ret.y) {
|
||
// 无标题的窗口,比如deepin-editor, dconf-editor等
|
||
WindowFrameExtents windowFrameRect = getWindowFrameExtents(xid);
|
||
if (!windowFrameRect.isNull()) {
|
||
int x = ret.x + windowFrameRect.Left;
|
||
int y = ret.y + windowFrameRect.Top;
|
||
int width = ret.width - (windowFrameRect.Left + windowFrameRect.Right);
|
||
int height = ret.height - (windowFrameRect.Top + windowFrameRect.Bottom);
|
||
ret.x = x;
|
||
ret.y = y;
|
||
ret.width = width;
|
||
ret.height = height;
|
||
}
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
XWindow XCBUtils::getDecorativeWindow(XWindow xid)
|
||
{
|
||
XWindow winId = xid;
|
||
for (int i = 0; i < 10; i++) {
|
||
xcb_query_tree_cookie_t cookie = xcb_query_tree(m_connect, winId);
|
||
std::shared_ptr<xcb_query_tree_reply_t> reply(
|
||
xcb_query_tree_reply(m_connect, cookie, nullptr),
|
||
[=](xcb_query_tree_reply_t* reply){free(reply);}
|
||
);
|
||
if (!reply) return 0;
|
||
if (reply->root == reply->parent) return winId;
|
||
|
||
winId = reply->parent;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
WindowFrameExtents XCBUtils::getWindowFrameExtents(XWindow xid)
|
||
{
|
||
xcb_atom_t perp = getAtom("_NET_FRAME_EXTENTS");
|
||
xcb_get_property_cookie_t cookie = xcb_get_property(m_connect, false, xid, perp, XCB_ATOM_CARDINAL, 0, 4);
|
||
std::shared_ptr<xcb_get_property_reply_t> reply(
|
||
xcb_get_property_reply(m_connect, cookie, nullptr),
|
||
[=](xcb_get_property_reply_t* reply){free(reply);}
|
||
);
|
||
if (!reply || reply->format == 0) {
|
||
perp = getAtom("_GTK_FRAME_EXTENTS");
|
||
cookie = xcb_get_property(m_connect, false, xid, perp, XCB_ATOM_CARDINAL, 0, 4);
|
||
reply.reset(xcb_get_property_reply(m_connect, cookie, nullptr), [=](xcb_get_property_reply_t* reply){free(reply);});
|
||
if (!reply)
|
||
return WindowFrameExtents();
|
||
}
|
||
|
||
if (reply->format != 32 || reply->value_len != 4) {
|
||
return WindowFrameExtents();
|
||
}
|
||
uint32_t *data = static_cast<uint32_t *>(xcb_get_property_value(reply.get()));
|
||
|
||
if (!data)
|
||
return WindowFrameExtents();
|
||
|
||
WindowFrameExtents winFrame(data[0], data[1], data[2], data[3]);
|
||
return winFrame;
|
||
}
|
||
|
||
XWindow XCBUtils::getActiveWindow()
|
||
{
|
||
XWindow ret;
|
||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_active_window(&m_ewmh, m_screenNum);
|
||
if (!xcb_ewmh_get_active_window_reply(&m_ewmh, cookie, &ret, nullptr)) {
|
||
std::cout << "getActiveWindow error" << std::endl;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
void XCBUtils::setActiveWindow(XWindow xid)
|
||
{
|
||
xcb_ewmh_set_active_window(&m_ewmh, m_screenNum, xid);
|
||
}
|
||
|
||
void XCBUtils::changeActiveWindow(XWindow newActiveXid)
|
||
{
|
||
xcb_ewmh_request_change_active_window(&m_ewmh, m_screenNum, newActiveXid, XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER, XCB_CURRENT_TIME, XCB_WINDOW_NONE);
|
||
flush();
|
||
}
|
||
|
||
void XCBUtils::restackWindow(XWindow xid)
|
||
{
|
||
xcb_ewmh_request_restack_window(&m_ewmh, m_screenNum, xid, 0, XCB_STACK_MODE_ABOVE);
|
||
}
|
||
|
||
std::list<XWindow> XCBUtils::getClientList()
|
||
{
|
||
std::list<XWindow> ret;
|
||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_client_list(&m_ewmh, m_screenNum);
|
||
xcb_ewmh_get_windows_reply_t reply;
|
||
if (xcb_ewmh_get_client_list_reply(&m_ewmh, cookie, &reply, nullptr)) {
|
||
for (uint32_t i = 0; i < reply.windows_len; i++) {
|
||
ret.push_back(reply.windows[i]);
|
||
}
|
||
|
||
xcb_ewmh_get_windows_reply_wipe(&reply);
|
||
} else {
|
||
std::cout << "getClientList error" << std::endl;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
std::list<XWindow> XCBUtils::getClientListStacking()
|
||
{
|
||
std::list<XWindow> ret;
|
||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_client_list_stacking(&m_ewmh, m_screenNum);
|
||
xcb_ewmh_get_windows_reply_t reply;
|
||
if (xcb_ewmh_get_client_list_stacking_reply(&m_ewmh, cookie, &reply, nullptr)) {
|
||
for (uint32_t i = 0; i < reply.windows_len; i++) {
|
||
ret.push_back(reply.windows[i]);
|
||
}
|
||
|
||
xcb_ewmh_get_windows_reply_wipe(&reply);
|
||
} else {
|
||
std::cout << "getClientListStacking error" << std::endl;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
std::vector<XCBAtom> XCBUtils::getWMState(XWindow xid)
|
||
{
|
||
std::vector<XCBAtom> ret;
|
||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_state(&m_ewmh, xid);
|
||
xcb_ewmh_get_atoms_reply_t reply; // a list of Atom
|
||
if (xcb_ewmh_get_wm_state_reply(&m_ewmh, cookie, &reply, nullptr)) {
|
||
for (uint32_t i = 0; i < reply.atoms_len; i++) {
|
||
ret.push_back(reply.atoms[i]);
|
||
}
|
||
|
||
xcb_ewmh_get_atoms_reply_wipe(&reply);
|
||
} 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(&m_ewmh, xid);
|
||
xcb_ewmh_get_atoms_reply_t reply; // a list of Atom
|
||
if (xcb_ewmh_get_wm_window_type_reply(&m_ewmh, cookie, &reply, nullptr)) {
|
||
for (uint32_t i = 0; i < reply.atoms_len; i++) {
|
||
ret.push_back(reply.atoms[i]);
|
||
}
|
||
|
||
xcb_ewmh_get_atoms_reply_wipe(&reply);
|
||
} else {
|
||
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(&m_ewmh, xid);
|
||
xcb_ewmh_get_atoms_reply_t reply; // a list of Atoms
|
||
if (xcb_ewmh_get_wm_allowed_actions_reply(&m_ewmh, cookie, &reply, nullptr)) {
|
||
for (uint32_t i = 0; i < reply.atoms_len; i++) {
|
||
ret.push_back(reply.atoms[i]);
|
||
}
|
||
|
||
xcb_ewmh_get_atoms_reply_wipe(&reply);
|
||
} else {
|
||
std::cout << xid << " getWMAllowedActions error" << std::endl;
|
||
}
|
||
|
||
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(&m_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(&m_ewmh, xid);
|
||
xcb_ewmh_get_utf8_strings_reply_t reply;
|
||
if (xcb_ewmh_get_wm_name_reply(&m_ewmh, cookie, &reply, nullptr)) {
|
||
ret.assign(reply.strings, reply.strings_len);
|
||
// 释放utf8_strings_reply分配的内存
|
||
xcb_ewmh_get_utf8_strings_reply_wipe(&reply);
|
||
} else {
|
||
std::cout << xid << " getWMName error" << std::endl;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
uint32_t XCBUtils::getWMPid(XWindow xid)
|
||
{
|
||
// NOTE(black_desk): code copy from https://gitlab.gnome.org/GNOME/metacity/-/merge_requests/13/diffs
|
||
|
||
XResClientIdSpec spec = {
|
||
.client = xid,
|
||
.mask = XRES_CLIENT_ID_PID_MASK,
|
||
};
|
||
|
||
std::shared_ptr<Display> dpy = {
|
||
XOpenDisplay(nullptr),
|
||
[](Display *p){ XCloseDisplay(p); },
|
||
};
|
||
|
||
long num_ids;
|
||
XResClientIdValue *client_ids;
|
||
XResQueryClientIds(dpy.get(),
|
||
1,
|
||
&spec,
|
||
&num_ids,
|
||
&client_ids);
|
||
|
||
pid_t pid = -1;
|
||
for (long i = 0; i < num_ids; i++) {
|
||
if (client_ids[i].spec.mask == XRES_CLIENT_ID_PID_MASK) {
|
||
pid = XResGetClientPid(&client_ids[i]);
|
||
break;
|
||
}
|
||
}
|
||
|
||
XResClientIdsDestroy(num_ids, client_ids);
|
||
return pid;
|
||
}
|
||
|
||
std::string XCBUtils::getWMIconName(XWindow xid)
|
||
{
|
||
std::string ret;
|
||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_icon_name(&m_ewmh, xid);
|
||
xcb_ewmh_get_utf8_strings_reply_t reply;
|
||
if (!xcb_ewmh_get_wm_icon_name_reply(&m_ewmh, cookie, &reply, nullptr)) {
|
||
std::cout << xid << " getWMIconName error" << std::endl;
|
||
}
|
||
|
||
ret.assign(reply.strings);
|
||
|
||
return ret;
|
||
}
|
||
|
||
WMIcon XCBUtils::getWMIcon(XWindow xid)
|
||
{
|
||
WMIcon wmIcon{};
|
||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_icon(&m_ewmh, xid);
|
||
xcb_ewmh_get_wm_icon_reply_t reply;
|
||
xcb_generic_error_t* error;
|
||
|
||
auto ret = xcb_ewmh_get_wm_icon_reply(&m_ewmh, cookie, &reply, &error);
|
||
|
||
if (error) {
|
||
std::cout << "failed to get wm icon" << error->error_code;
|
||
std::free(error);
|
||
return wmIcon;
|
||
}
|
||
|
||
if (ret) {
|
||
auto fcn = [](xcb_ewmh_wm_icon_iterator_t it) {
|
||
// https://specifications.freedesktop.org/wm-spec/wm-spec-1.3.html#idm45582154990752
|
||
// The first two cardinals are width, height. Data is in rows, left to right and top to bottom
|
||
// Two cardinals means width and heighr, not offset.
|
||
const auto size = it.width * it.height;
|
||
std::vector<uint32_t> ret(size);
|
||
// data数据是按行从左至右,从上至下排列
|
||
uint32_t *data = it.data;
|
||
if (!data) {
|
||
return ret;
|
||
}
|
||
|
||
std::copy_n(data, size, ret.begin());
|
||
return ret;
|
||
};
|
||
|
||
// 获取icon中size最大的图标
|
||
xcb_ewmh_wm_icon_iterator_t iter = xcb_ewmh_get_wm_icon_iterator(&reply);
|
||
xcb_ewmh_wm_icon_iterator_t wmIconIt{0, 0, nullptr};
|
||
for (; iter.rem; xcb_ewmh_get_wm_icon_next(&iter)) {
|
||
const uint32_t size = iter.width * iter.height;
|
||
if (size > 0 && size > wmIconIt.width * wmIconIt.height) {
|
||
wmIconIt = iter;
|
||
}
|
||
}
|
||
|
||
wmIcon = WMIcon{wmIconIt.width, wmIconIt.height, fcn(wmIconIt)};
|
||
|
||
xcb_ewmh_get_wm_icon_reply_wipe(&reply); // clear
|
||
}
|
||
|
||
return wmIcon;
|
||
}
|
||
|
||
XWindow XCBUtils::getWMClientLeader(XWindow xid)
|
||
{
|
||
XWindow ret = 0;
|
||
XCBAtom atom = getAtom("WM_CLIENT_LEADER");
|
||
void *value = getPropertyValue(xid, atom, XCB_ATOM_INTEGER);
|
||
if (value) {
|
||
ret = *(XWindow*)(value);
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
void XCBUtils::requestCloseWindow(XWindow xid, uint32_t timestamp)
|
||
{
|
||
xcb_ewmh_request_close_window(&m_ewmh, m_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(&m_ewmh, xid);
|
||
if (!xcb_ewmh_get_wm_desktop_reply(&m_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(&m_ewmh, xid, desktop);
|
||
}
|
||
|
||
void XCBUtils::setCurrentWMDesktop(uint32_t desktop)
|
||
{
|
||
xcb_ewmh_set_current_desktop(&m_ewmh, m_screenNum, desktop);
|
||
}
|
||
|
||
void XCBUtils::changeCurrentDesktop(uint32_t newDesktop, uint32_t timestamp)
|
||
{
|
||
xcb_ewmh_request_change_current_desktop(&m_ewmh, m_screenNum, newDesktop, timestamp);
|
||
}
|
||
|
||
uint32_t XCBUtils::getCurrentWMDesktop()
|
||
{
|
||
uint32_t ret;
|
||
xcb_get_property_cookie_t cookie = xcb_ewmh_get_current_desktop(&m_ewmh, m_screenNum);
|
||
if (!xcb_ewmh_get_current_desktop_reply(&m_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(m_connect, xid);
|
||
xcb_generic_error_t **errStore = nullptr;
|
||
xcb_get_geometry_reply_t *reply = xcb_get_geometry_reply(m_connect, cookie, errStore);
|
||
if (reply) {
|
||
// 正常获取窗口geometry则判定为good
|
||
if (!errStore) {
|
||
ret = true;
|
||
} else {
|
||
free(errStore);
|
||
}
|
||
|
||
free(reply);
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
// TODO XCB下无_MOTIF_WM_HINTS属性
|
||
MotifWMHints XCBUtils::getWindowMotifWMHints(XWindow xid)
|
||
{
|
||
XCBAtom atomWmHints = getAtom("_MOTIF_WM_HINTS");
|
||
xcb_get_property_cookie_t cookie = xcb_get_property(m_connect, false, xid, atomWmHints, atomWmHints, 0, 5);
|
||
std::unique_ptr<xcb_get_property_reply_t> reply(xcb_get_property_reply(m_connect, cookie, nullptr));
|
||
if (!reply || reply->format != 32 || reply->value_len != 5)
|
||
return MotifWMHints{0, 0, 0, 0, 0};
|
||
|
||
uint32_t *data = static_cast<uint32_t *>(xcb_get_property_value(reply.get()));
|
||
MotifWMHints ret;
|
||
ret.flags = data[0];
|
||
ret.functions = data[1];
|
||
ret.decorations = data[2];
|
||
ret.inputMode = data[3];
|
||
ret.status = data[4];
|
||
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(m_connect, xid);
|
||
if (!xcb_icccm_get_wm_transient_for_reply(m_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(&m_ewmh, xid);
|
||
if (!xcb_ewmh_get_wm_user_time_reply(&m_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(&m_ewmh, xid);
|
||
if (!xcb_ewmh_get_wm_user_time_window_reply(&m_ewmh, cookie, &ret, nullptr)) {
|
||
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(m_connect, xid);
|
||
xcb_icccm_get_wm_class_reply_t reply;
|
||
reply.instance_name = nullptr;
|
||
reply.class_name = nullptr;
|
||
xcb_icccm_get_wm_class_reply(m_connect, cookie, &reply, nullptr); // 返回值为0不一定表示失败, 故不做返回值判断
|
||
if (reply.class_name)
|
||
ret.className.assign(reply.class_name);
|
||
|
||
if (reply.instance_name)
|
||
ret.instanceName.assign(reply.instance_name);
|
||
|
||
if (reply.class_name || reply.instance_name) {
|
||
xcb_icccm_get_wm_class_reply_wipe(&reply);
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
void XCBUtils::minimizeWindow(XWindow xid)
|
||
{
|
||
uint32_t data[2];
|
||
data[0] = XCB_ICCCM_WM_STATE_ICONIC;
|
||
data[1] = XCB_NONE;
|
||
xcb_ewmh_send_client_message(m_connect, xid, getRootWindow(),getAtom("WM_CHANGE_STATE"), 2, data);
|
||
flush();
|
||
}
|
||
|
||
void XCBUtils::maxmizeWindow(XWindow xid)
|
||
{
|
||
xcb_ewmh_request_change_wm_state(&m_ewmh
|
||
, m_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, m_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(m_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(m_connect,
|
||
xid,
|
||
XCB_CW_EVENT_MASK,
|
||
&value);
|
||
flush();
|
||
|
||
xcb_generic_error_t *error = xcb_request_check(m_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 = m_atoms.find(name);
|
||
if (search != m_atoms.end()) {
|
||
atom = search->second;
|
||
}
|
||
|
||
return atom;
|
||
}
|
||
|
||
std::string AtomCache::getName(XCBAtom atom)
|
||
{
|
||
std::string ret;
|
||
auto search = m_atomNames.find(atom);
|
||
if (search != m_atomNames.end()) {
|
||
ret = search->second;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
void AtomCache::store(std::string name, XCBAtom value)
|
||
{
|
||
m_atoms[name] = value;
|
||
m_atomNames[value] = name;
|
||
}
|