linux自增窗口状态——KwindowSystem篇

前言:

之前曾翻译过 X Desktop Group,今天分享一下如何自定义状态;

此篇讲述如何通过kwindowsystem新增状态,以及设置状态的过程;

窗口有不同的属性、状态、允许Action等等信息,以使窗口管理器来确定窗口的装饰、堆叠位置和其他行为,而我们可以通过xprop命令(之前曾讲过xprop)进行查看窗口允许的操作、属性状态等等。

正文:

一个客户端窗口除了自己本身进行UI设置和功能设置外,还需要设置不同的属性来供其他组件进行管理,或者联动,尤其是操作系统本身的组件,肯定需要通过设置property、Actions和status等信息,方便窗口管理器来确定窗口的装饰、堆叠位置和其他行为,更好的管理窗口。今天以_NET_WM_STATE为例,讲述如何增加自定义状态,当然举一反三自定义其他的类型属性,都是差不多的原理,不过还是建议使用已经定义的属性,因为比较规范,后期会分享一下,什么时候定义属性什么时候定义接口。

  • 整体流程

  • 代码部分
  1. KwindowSystem

如果我们想为窗口增加新的属性,可以通过在KwindowSystem的代码中,增加新的属性,进行处理,以下是增加属性的步骤:

1. atoms_p.h
//此文件保存了所有窗口属性用到的枚举变量,当我们需要增加新的自定义属性的时候,需要在此文件中找到自己想要新增属性对应类型的位置,此次我们新增的是应用窗口状态,所以在应用窗口状态下,新增如下代码:
ENUM(_NET_WM_STATE_NEW_TEST)  
//注意全部大写,虽然也可以小写,但最好符合规范;
//NEW_TEST是我自己起的名字,不是固定的可以更换,一般前不同的类型半部分都有对应的固定部分

2. netwm_def.h
//此文件中定义了NETRootInfo 和NETWinInfo的基类NET,用于检索和修改窗口的属性。保持命名空间相对干净,所有枚举都在这里定义。
//我们需要在这里声明好我们先前新增的属性对应的枚举值,这样外部调用的时候,可以直接NET::***,设置窗口状态;
//我们依然找到自己新增的属性对应类型的位置,我新增的是状态,故增加在state中,代码如下:
enum State {
       TestState = 1u << 0 //这里名字没有固定部分,根据自己需要进行起名即可
};

以上准备工作都已经完成,下面是真正进行处理窗口设置了该属性后,KwindowSystem需要做的工作。

3.netwm.cpp
//此文件中定义了NETRootInfo类,主要客户端和窗口管理器提供了一个通用API设置/读取/更改由NET窗口定义的根窗口的属性;
//在这文件中,重载了以下五个函数,为方便起见而提供,它们之间的区别仅在于它接受的参数,第一个参数代表接受的类型,第二个参数on,如果为true则设置相应属性,如果为false,则删除属性:
void setSupported(NET::Property property, bool on = true);
void setSupported(NET::Property2 property, bool on = true);
void setSupported(NET::WindowTypeMask property, bool on = true);
void setSupported(NET::State property, bool on = true);
void setSupported(NET::Action property, bool on = true);
//以上五个函数内部没有复杂的处理逻辑,所有的property、state、Action和mask都是在下面这个函数进行设置的,故我们只需要将新增属性的过滤写在下面的函数中:
void NETRootInfo::setSupported()
{
    //这个函数仅是设置支持什么属性,此函数中处理了所有类型,所以只需要找到自己对应的部分,增加设置即可,我的是state,故代码如下:
     if (p->properties & WMState) {
        atoms[pnum++] = p->atom(_NET_WM_STATE);
        //这里进行判断是否是新增的属性,如果是,则加入atoms中,因为一个窗口会有很多属性状态,所以需要存储在一起在函数尾部xcb_change_property()中设置;
         if (p->states & TestState) {         
            atoms[pnum++] =  p>atom(_NET_WM_STATE_NEW_TEST);
        }
}

****************************************************************
//上面只是设置,因为属性有更新的情况,故我们还需要在以下函数中进行处理:
void NETRootInfo::updateSupportedProperties(xcb_atom_t atom)
{
    //此函数的方式和上一个设置没有区别,只不过判断条件变化了
    if (atom == p->atom(_NET_WM_STATE_NEW_TEST)) {
        p->states |= TestState;
}

****************************************************************
//上面设置完成后,还需要在新增属性对应的处理函数中,增加处理,因为KwindowSystem中会有对外设置状态的接口,如我的是设置状态,故在下面的函数中设置:
void NETWinInfo::setState(NET::States state, NET::States mask)
{
    //此函数设置应用程序窗口的状态
    xcb_client_message_event_t event;
    event.response_type = XCB_CLIENT_MESSAGE;
    event.format = 32;
    event.sequence = 0;
    event.window = p->window;
    event.type = p->atom(_NET_WM_STATE);
    event.data.data32[3] = 0;
    event.data.data32[4] = 0;
    //以上是x下设置窗口状态的流程,指明窗口,设置状态
    //注意:以下代码的外层还有一个if,所以我们在if中增加此部分代码,在else中也要进行判断,此处就不列出了;
    if ((mask & TestState) && ((p->state & TestState) != (state & TestState))) {
        event.data.data32[0] = (state & TestState) ? 1 : 0;
        event.data.data32[1] = p->atom(_NET_WM_STATE_NEW_TEST);
        event.data.data32[2] = 0l;
        xcb_send_event(p->conn, false, p->root, netwm_sendevent_mask, (const char *) &event);
    }
}

****************************************************************
//还需要在以下函数中进行处理:
void NETWinInfo::event(xcb_generic_event_t *event, NET::Properties *properties, NET::Properties2 *properties2)
{
    //此函数接受传入的xcb_generic_event_t,并在传入的参数中返回更新的属性。
    if ((xcb_atom_t) message->data.data32[i] == p->atom(_NET_WM_STATE_NEW_TEST)) {
                    mask |= TestState;
                }
}

void NETWinInfo::update(NET::Properties dirtyProperties, NET::Properties2 dirtyProperties2)
{
    if (_NET_WM_STATE_NEW_TEST) {
                p->state |= TestState;
            }
}
以上便是KwindowSystem中需要做的所有工作;
  1. 设置属性demo
    1. pro文件
QT       += core gui  x11extras //x11extras也一定要添加
QT       += KWindowSystem //这个一定要添加

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11
PKGCONFIG += x11 xext
CONFIG += c++14
CONFIG += qt warn_on
CONFIG += release
CONFIG += link_pkgconfig
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    utils.cpp \
    widget.cpp

HEADERS += \
    utils.h \
    widget.h

FORMS += \
    widget.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

  1. utils.h
#include <QObject>
//这两个头文件注意
#include <netwm.h>
#include <netwm_def.h>
class Utils : public QObject
{
public:
    static void setProperty(int wid);
};

utils.cpp
#include "utils.h"

#include <QDebug>
#include <QtX11Extras/QX11Info>
#include <X11/extensions/shape.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <iostream>
#include <kwindowsystem.h>

void Utils::setProperty(int wid)
{
    //这里直接调用setState接口,即上面提到的接口,传入窗口id和设置的state即可
    KWindowSystem::setState( wid, NET::TestState);
}
  1. widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
class QPushButton;
QT_END_NAMESPACE
namespace Ui { class Widget; }
class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);;
    ~Widget();

private:
    Ui::Widget *ui;
    QPushButton *m_test;
};
#endif // WIDGET_H

    1. widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include "utils.h"
#include <QDebug>
#include <iostream>
#include <QApplication>
#include <QPushButton>
#include <QVBoxLayout>
#include <QDebug>

#include <QtX11Extras/QX11Info>
#include <X11/extensions/shape.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <iostream>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->resize(500, 700);
    m_test = new QPushButton(this);
    m_test->setText("setProperty");

    QVBoxLayout *m_layout = new QVBoxLayout;
    m_layout->addStretch();
    m_layout->addWidget(m_test, 0 , Qt::AlignHCenter);
    m_layout->addStretch();
    this->setLayout(m_layout);

    connect(m_test, &QPushButton::clicked, this, [=] {
        qDebug() << "this->winId()" <<this->winId();
        Utils::setProperty(this->winId());
    });
}

Widget::~Widget()
{
    delete ui;
}


  1. main.cpp
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

  1. 获取属性demo
    1. pro文件
QT       += core gui  x11extras dbus
QT       += KWindowSystem


greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
PKGCONFIG += x11 xext
CONFIG += c++14
CONFIG += qt warn_on
CONFIG += release
CONFIG += link_pkgconfig
TARGET = getProperty
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0


SOURCES += \
        main.cpp \
        widget.cpp \
    utils.cpp

HEADERS += \
        widget.h \
    utils.h

FORMS += \
        widget.ui

    1. utils.h
#include <QObject>
#include <netwm.h>
#include <netwm_def.h>
class Utils : public QObject
{
public:
    static char * getProperty();

};

    1. utils.cpp
#include "utils.h"

#include <QDebug>
#include <QtX11Extras/QX11Info>
#include <X11/extensions/shape.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <iostream>
#include <QtDBus/QDBusMessage>
#include <QtDBus/QDBusConnection>
#include <kwindowsystem.h>
char * Utils::getProperty()
{
    // get window id 根据自己的需要获取相应的id
    auto wid = 0;
    
    const auto screen = QX11Info::appScreen();
    Window rootWindow = QX11Info::appRootWindow(screen);
    const NET::Properties properties =
            NET::WMDesktop |
            NET::WMState |
            NET::WMWindowType |
            NET::WMStrut |
            NET::WMName |
            NET::WMIconGeometry |
            NET::WMIcon |
            NET::WMPid |
            NET::WMIconName;
    const NET::Properties2 properties2 =
            NET::WM2BlockCompositing |
            NET::WM2WindowClass |
            NET::WM2WindowRole |
            NET::WM2UserTime |
            NET::WM2StartupId |
            NET::WM2ExtendedStrut |
            NET::WM2Opacity |
            NET::WM2FullscreenMonitors |
            NET::WM2FrameOverlap |
            NET::WM2GroupLeader |
            NET::WM2Urgency |
            NET::WM2Input |
            NET::WM2Protocols |
            NET::WM2InitialMappingState |
            NET::WM2IconPixmap |
            NET::WM2OpaqueRegion |
            NET::WM2DesktopFileName |
            NET::WM2GTKFrameExtents;

    xcb_connection_t *c = QX11Info::connection();
    NETWinInfo* info = new  NETWinInfo(c, wid, rootWindow,properties, properties2, NET::Role::Client);
    if(info->state() & NET::TestState)
    {
        qDebug() << "set is success";
    }
    else
    {
        qDebug() << "set is fail";
    }
    return nullptr;
}
    1. widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
class QPushButton;
QT_END_NAMESPACE
namespace Ui { class Widget; }
class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);;
    ~Widget();

private:
    Ui::Widget *ui;
    QPushButton *m_getProperty;
};
#endif // WIDGET_H

    1. widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include "utils.h"
#include <QDebug>
#include <iostream>
#include <QApplication>
#include <QPushButton>
#include <QVBoxLayout>
#include <QDebug>

#include <QtX11Extras/QX11Info>
#include <X11/extensions/shape.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <iostream>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->resize(500, 700);
    qDebug() << "this id is " << this->winId();
    m_getProperty = new QPushButton(this);
    m_getProperty->setText("m_getProperty");

    QVBoxLayout *m_layout = new QVBoxLayout;
    m_layout->addStretch();
    m_layout->addWidget(m_getProperty, 0 , Qt::AlignHCenter);
    m_layout->addStretch();
    this->setLayout(m_layout);

    connect(m_getProperty, &QPushButton::clicked, this, [=] {
        qDebug() << "get value";
        Utils::getProperty();
    });
}

Widget::~Widget()
{
    delete ui;
}
    1. main.cpp
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

4. 谁获取谁设置

只是执行上面三步,会发现设置新增的属性后,使用xprop获取时看不见,还需要在调用放进行设置一次窗口属性,既可以看见了

以上代码都可以直接运行,可直接验证,当然不要忘记设置自己的属性哦~

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值