gpt4 book ai didi

c++ - Xlib。调整大小后重绘(重新填充)闪烁的形状窗口

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:31:46 25 4
gpt4 key购买 nike

我有实现闪烁边框的类(它每 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;
}

从代码中可以看出,当 choice 为 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 不起作用。

最佳答案

flicker all border after resize

使 XRectangle 矩形[4] 成为类字段。难怪它开始表现得很奇怪,因为即使在新矩形已应用于窗口后,“闪烁”功能仍会继续使用旧矩形。

how to hide/show window

这个比较棘手。

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);
}
}

关于c++ - Xlib。调整大小后重绘(重新填充)闪烁的形状窗口,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57169074/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com