我有一个实现闪烁边框的类(它每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不起作用。
答案 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);
}
}