Xlib。调整大小后重新绘制(重新填充)闪烁的成型窗口

时间:2019-07-23 17:04:22

标签: c++ linux x11 xlib

我有一个实现闪烁边框的类(它每0.5秒更改一次颜色)。一切正常,直到调整边框大小为止。调整边框大小时,边框的仅一部分会继续闪烁。我想这是我的Expose事件处理程序或调整大小函数中某个地方的问题,但是我找不到确切的位置。 主程序启动用于创建边框的单独线程。边框由主线程控制:

#include "LinuxBorderWindow.h"
#include <thread>
#include <iostream>

#define W_WIDTH 640
#define W_HEIGHT 480

#define X_POS 100
#define Y_POS 120
#define BORDER_WIDTH 2

LinuxBorderWindow* border;

void threadFunc()
{
    border->SetPosition(X_POS, Y_POS, X_POS + W_WIDTH, Y_POS + W_HEIGHT);
    border->Start();
}

int main(int argc, char *argv[])
{
    border = new LinuxBorderWindow();

    std::thread(threadFunc).detach();

    int choice = 0;
    bool isExit = false;
    while(!isExit)
    {
        std::cout << "Input action" << std::endl;
        std::cin >> choice;
        switch(choice)
        {
        case 1:
            border->MoveBorder(X_POS + 100, Y_POS + 200);
            break;
        case 2:
            border->ResizeBorder(X_POS + 100, Y_POS + 200, W_WIDTH - 100, W_HEIGHT + 200);
            break;
        case 3:
            border->ResizeBorder(0, 0, W_WIDTH + 100, W_HEIGHT + 200);
            break;
        case 4:
            border->ShowBorder(false);
            break;
        case 5:
            border->ShowBorder(true);
            break;
        case 0:
            isExit = true;
            break;
        }
    }

    delete border;

    return 0;
}

从代码中可以看出,当选择为2或3时,将调用resize函数。在这种情况下,边框将停止正确闪烁。

这是边界类别:

#pragma once

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/shape.h>

class LinuxBorderWindow
{
public:
    LinuxBorderWindow();
    ~LinuxBorderWindow();

    void Start();
    void Stop();
    void ShowBorder(bool show);
    void MoveBorder(int x, int y);
    void SetPosition(int x1, int y1, int x2, int y2);
    void ResizeBorder(int x1, int y1, int x2, int y2);
    void UpdateRegionSizeAndPos();

private:
    void CreateBorder();

    XRectangle m_windowRect;
    short unsigned int m_borderThickness;

    Window m_window;
    Display* m_display;

    XColor _darkOrangeColor;
    XColor _lightOrangeColor;

    bool m_highlightFrame = false;
};

.cpp文件:

#include "LinuxBorderWindow.h"
#include <thread>
#include <sys/select.h>
#include <cmath>

static int wait_fd(int fd, double seconds)
{
    struct timeval tv;
    fd_set in_fds;
    FD_ZERO(&in_fds);
    FD_SET(fd, &in_fds);
    tv.tv_sec = trunc(seconds);
    tv.tv_usec = (seconds - trunc(seconds))*1000000;
    return select(fd+1, &in_fds, 0, 0, &tv);
}

int XNextEventTimeout(Display *display, XEvent *event, double seconds)
{
    if (XPending(display) || wait_fd(ConnectionNumber(display),seconds))
    {
        XNextEvent(display, event);
        return 0;
    }
    else
    {
        return 1;
    }
}

constexpr short frameThickness = 2;

void LinuxBorderWindow::CreateBorder()
{
    GC gc;
    XGCValues gcv = {0};

    Window defaultRootWindow = DefaultRootWindow(m_display);

    m_window = XCreateSimpleWindow(m_display, defaultRootWindow, m_windowRect.x, m_windowRect.y,
                               m_windowRect.width, m_windowRect.height, m_borderThickness, 0, _darkOrangeColor.pixel);
    gcv.line_width = m_borderThickness;
    gc = XCreateGC(m_display, m_window, GCLineWidth, &gcv);

    XSelectInput(m_display, m_window, ExposureMask);
    Atom window_type = XInternAtom(m_display, "_NET_WM_WINDOW_TYPE", False);
    long value = XInternAtom(m_display, "_NET_WM_WINDOW_TYPE_DOCK", False);
    XChangeProperty(m_display, m_window, window_type, XA_ATOM, 32, PropModeReplace, (unsigned char *) &value, 1);
    Atom wm_delete_window = XInternAtom(m_display, "WM_DELETE_WINDOW", 0);
    XSetWMProtocols(m_display, m_window, &wm_delete_window, 1);
    XRectangle rectangles[4] =
    {
        { 0, 0, m_windowRect.width, m_borderThickness },
        { 0, 0, m_borderThickness, m_windowRect.height },
        { 0, (short)(m_windowRect.height - m_borderThickness), m_windowRect.width, m_borderThickness },
        { (short)(m_windowRect.width - m_borderThickness), 0, m_borderThickness, m_windowRect.height }
    };
    XserverRegion region = XFixesCreateRegion(m_display, rectangles, 4);
    XFixesSetWindowShapeRegion(m_display, m_window, ShapeBounding, 0, 0, region);
    XMapWindow(m_display, m_window);
    XFlush(m_display);
    XSync(m_display, False);

    //std::thread([this, gc]
    {
        bool run = true;
        while(run)
        {
            XEvent xe;
            if(::XNextEventTimeout(m_display, &xe, 0.5))
            {
                m_highlightFrame = !m_highlightFrame;
                xe.type = Expose;
                xe.xexpose.count = 0;
            }
            switch (xe.type)
            {
                case Expose:
                {
                    XSetForeground(m_display, gc, m_highlightFrame ? _lightOrangeColor.pixel : _darkOrangeColor.pixel);
                    XFillRectangles(m_display, m_window, gc, rectangles, 4);
                    XSync(m_display, False);
                    break;
                }
                case ClientMessage:
                {
                    if (xe.xclient.message_type == XInternAtom(m_display, "WM_PROTOCOLS", 1)
                    && (Atom)xe.xclient.data.l[0] == XInternAtom(m_display, "WM_DELETE_WINDOW", 1))
                    {
                        run = false;
                    }
                    break;
                }
                default:
                    break;
            }
        }
    }//).detach();
}

LinuxBorderWindow::LinuxBorderWindow()
{
    m_borderThickness = ::frameThickness;

    m_display = XOpenDisplay(NULL);

    char orangeDark[] = "#FF8000";
    char orangeLight[] = "#FFC90E";
    Colormap colormap = DefaultColormap(m_display, 0);
    XParseColor(m_display, colormap, orangeDark, &_darkOrangeColor);
    XAllocColor(m_display, colormap, &_darkOrangeColor);
    XParseColor(m_display, colormap, orangeLight, &_lightOrangeColor);
    XAllocColor(m_display, colormap, &_lightOrangeColor);
}

LinuxBorderWindow::~LinuxBorderWindow()
{
    if(m_display != NULL)
    {
        Stop();
        XCloseDisplay(m_display);
    }
}

void LinuxBorderWindow::Start()
{
    CreateBorder();
}

void LinuxBorderWindow::Stop()
{
    XWindowAttributes xwa;
    XGetWindowAttributes(m_display, m_window, &xwa);
    if(xwa.map_state == IsViewable)
    {
        XUnmapWindow(m_display, m_window);
        XDestroyWindow(m_display, m_window);
    }
}

void LinuxBorderWindow::SetPosition(int x1, int y1, int x2, int y2)
{
    m_windowRect.x = (short)x1;
    m_windowRect.y = (short)y1;
    m_windowRect.width = (unsigned short)(x2 - x1);
    m_windowRect.height = (unsigned short)(y2 - y1);
}


void LinuxBorderWindow::ShowBorder(bool show)
{
    XWindowAttributes xwa;
    XGetWindowAttributes(m_display, m_window, &xwa);

    if(show && xwa.map_state != IsViewable)
    {
        XMapWindow(m_display, m_window);
    }
    else if(show == false && xwa.map_state == IsViewable)
    {
        XUnmapWindow(m_display, m_window);
    }
}
void LinuxBorderWindow::MoveBorder(int x, int y)
{
    m_windowRect.x = (short)x;
    m_windowRect.y = (short)y;
    XMoveWindow(m_display, m_window, x, y);
}

void LinuxBorderWindow::ResizeBorder(int x1, int y1, int x2, int y2)
{
    SetPosition(int x1, int y1, int x2, int y2)
    XMoveResizeWindow(m_display, m_window, m_windowRect.x, m_windowRect.y, m_windowRect.width, m_windowRect.height);
    XRectangle rectangles[4] =
    {
        { 0, 0, m_windowRect.width, m_borderThickness },
        { 0, 0, m_borderThickness, m_windowRect.height },
        { 0, (short)(m_windowRect.height - m_borderThickness), m_windowRect.width, m_borderThickness },
        { (short)(m_windowRect.width - m_borderThickness), 0, m_borderThickness, m_windowRect.height }
    };
    XserverRegion region = XFixesCreateRegion(m_display, rectangles, 4);
    XFixesSetWindowShapeRegion(m_display, m_window, ShapeBounding, 0, 0, region);
}

所以我的主要问题是在调整大小后如何使所有边框闪烁,但不仅限于一部分?另外一个问题是如何隐藏/显示窗口(ShowBorder方法),因为XUnmapWindow / XMapWindow不起作用。

1 个答案:

答案 0 :(得分:1)

  

调整大小后闪烁所有边框

使XRectangle rectangles[4]为类字段。怪不得它开始变得怪异,因为“闪烁”功能即使在将新矩形应用于窗口之后仍继续使用旧矩形。

  

如何隐藏/显示窗口

这个比较棘手。

X11库不是线程安全的。这就是为什么当您尝试从不同线程在一个窗口上进行操作时,可能会发生各种奇怪的事情的原因。通常,基于std::thread的方法不是管理X11消息循环的正确方法。

这是更符合X11的应用程序,在执行相同操作:

#include "LinuxBorderWindow.h"
#include <sys/poll.h>



int main(int argc, char *argv[]) {
    enum {
        W_WIDTH = 640,
        W_HEIGHT = 480,
        X_POS = 100,
        Y_POS = 120,
        BORDER_WIDTH = 4,
        MSEC_DELAY = 500,
    };

    Display *m_display = XOpenDisplay(NULL);
    struct pollfd fd = { ConnectionNumber(m_display), POLLIN };

    LinuxBorderWindow *border =
        new LinuxBorderWindow(m_display, BORDER_WIDTH, X_POS, Y_POS,
                              X_POS + W_WIDTH, Y_POS + W_HEIGHT);
    bool run = true;
    while (run) {
        if (!XPending(m_display) && !poll(&fd, 1, MSEC_DELAY)) {
            border->BlinkBorder();
            continue;
        }
        XEvent xe = {0};
        XNextEvent(m_display, &xe);
        switch (xe.type) {
        case Expose:
            break;

        case KeyRelease:
            switch (XLookupKeysym(&xe.xkey, 0)) {
            case '1':
                border->ResizeBorder(X_POS + 100, Y_POS + 200);
                break;
            case '2':
                border->ResizeBorder(X_POS + 100, Y_POS + 200,
                                     W_WIDTH - 100, W_HEIGHT + 200);
                break;
            case '3':
                border->ResizeBorder(0, 0, W_WIDTH + 100, W_HEIGHT + 200);
                break;
            case '4':
                border->ShowBorder(false);
                break;
            case '5':
                border->ShowBorder(true);
                break;
            case '0':
                run = false;
                break;
            }
            break;
        }
    }

    delete border;
    return XCloseDisplay(m_display);
}
#pragma once

#include <climits>

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/extensions/shape.h>
#include <X11/extensions/Xfixes.h>

class LinuxBorderWindow {
public:
    LinuxBorderWindow(Display *display, short frameThickness,
                      int x1, int y1, int x2, int y2);
    ~LinuxBorderWindow();
    Window GetWindow() { return m_window; }

    void BlinkBorder();
    void ShowBorder(bool show);
    void ResizeBorder(int x1, int y1, int x2 = INT_MAX, int y2 = INT_MAX);

private:
    XColor m_darkOrangeColor;
    XColor m_lightOrangeColor;
    XRectangle m_windowRect;
    unsigned short m_borderThickness;

    bool m_highlightFrame = false;
    Display *m_display = nullptr;
    Window m_window = 0;
};
#include "LinuxBorderWindow.h"



static void SetAtom(Display *disp, Window hwnd, int mode,
                    const char *name, const char *atom) {
    Atom prop = XInternAtom(disp, atom, false);
    XChangeProperty(disp, hwnd, XInternAtom(disp, name, false),
                    XA_ATOM, 32, mode, (unsigned char *)&prop, 1);
}



LinuxBorderWindow::LinuxBorderWindow
(Display *display, short frameThickness, int x1, int y1, int x2, int y2)
: m_display(display), m_borderThickness(frameThickness) {
    Colormap colormap = DefaultColormap(m_display, 0);
    XParseColor(m_display, colormap, "#FF8000", &m_darkOrangeColor);
    XParseColor(m_display, colormap, "#FFC90E", &m_lightOrangeColor);
    XAllocColor(m_display, colormap, &m_darkOrangeColor);
    XAllocColor(m_display, colormap, &m_lightOrangeColor);

    ResizeBorder(x1, y1, x2, y2);
    m_window = XCreateSimpleWindow(m_display, DefaultRootWindow(m_display),
                                   m_windowRect.x, m_windowRect.y,
                                   m_windowRect.width, m_windowRect.height,
                                   0, 0, m_darkOrangeColor.pixel);
    ShowBorder(true);

    XSelectInput(m_display, m_window, ExposureMask | KeyReleaseMask);

    // TOOLBAR, since _NET_WM_WINDOW_TYPE_DOCK does not accept keyboard input
    SetAtom(m_display, m_window, PropModeReplace,
            "_NET_WM_WINDOW_TYPE", "_NET_WM_WINDOW_TYPE_TOOLBAR");

    // compensating for the lack of _NET_WM_WINDOW_TYPE_DOCK
    SetAtom(m_display, m_window, PropModeReplace,
            "_NET_WM_STATE", "_NET_WM_STATE_ABOVE");

    Atom wm_delete_window = XInternAtom(m_display, "WM_DELETE_WINDOW", 0);
    XSetWMProtocols(m_display, m_window, &wm_delete_window, 1);

    XMapWindow(m_display, m_window);
    XFlush(m_display);
    XSync(m_display, false);
}



LinuxBorderWindow::~LinuxBorderWindow() {
    if (m_display && m_window) {
        XUnmapWindow(m_display, m_window);
        XDestroyWindow(m_display, m_window);
    }
}



void LinuxBorderWindow::BlinkBorder() {
    XSetWindowBackground(m_display, m_window,
                        (m_highlightFrame = !m_highlightFrame)?
                         m_lightOrangeColor.pixel : m_darkOrangeColor.pixel);
    XClearWindow(m_display, m_window);
}



void LinuxBorderWindow::ShowBorder(bool show) {
    unsigned short thickness = (show)? m_borderThickness : 0;
    XRectangle rectangles[4] = {
        { 0, 0, m_windowRect.width, thickness },
        { 0, 0, thickness, m_windowRect.height },
        { 0, m_windowRect.height - thickness, m_windowRect.width, thickness },
        { m_windowRect.width - thickness, 0, thickness, m_windowRect.height },
    };
    XserverRegion rgn = XFixesCreateRegion(m_display, rectangles, 4);
    XFixesSetWindowShapeRegion(m_display, m_window, ShapeBounding, 0, 0, rgn);
    XFixesDestroyRegion(m_display, rgn);
}



void LinuxBorderWindow::ResizeBorder(int x1, int y1, int x2, int y2) {
    m_windowRect.x = x1;
    m_windowRect.y = y1;
    if (x2 != INT_MAX)
        m_windowRect.width = x2 - x1;
    if (y2 != INT_MAX)
        m_windowRect.height = y2 - y1;
    if (m_window) {
        XMoveResizeWindow(m_display, m_window, m_windowRect.x, m_windowRect.y,
                          m_windowRect.width, m_windowRect.height);
        ShowBorder(true);
    }
}
相关问题