当前位置: 首页 > news >正文

九、异形窗口

文章目录

    • 1.创建没有标题栏的窗口
    • 2.实现一个五角星窗口
    • 3.实现一个圆形窗口
    • 4.圆形渐变窗口

到目前为止,我们创建的所有窗口都是长方形的窗口。在实际开发中,有时我们可能需要创建非矩形窗口,圆角矩形窗口,圆形、椭圆形或其它类型的窗口。在xlib所提供的方法中,没有创建圆角矩形或是异形窗口的接口。此时我们需要使用region结合XShapeCombineRegion函数实现异形窗口的绘制。

1.创建没有标题栏的窗口

在之前所有的示例中,调用XCreateWindow或是XCreateSimpleWindow,创建出来的窗口都会默认带有标题栏、边框等信息;这些“额外”的信息是由操作系统的窗口管理器实现的。在某些需求下,我们不需要操作系统自带的标题栏。在窗口创建完成后,我们可以使用XChangeProperty来改变窗口样式。一个简单不带有标题栏的窗口如下:

#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>int main() {Display *display;Window window;int screen;/* 打开与X服务器的连接 */display = XOpenDisplay(NULL);if (display == NULL) {fprintf(stderr, "无法打开X显示器\n");exit(1);}screen = DefaultScreen(display);/* 创建窗口 */window = XCreateSimpleWindow(display, RootWindow(display, screen), 10, 10, 400, 300, 1,BlackPixel(display, screen), WhitePixel(display, screen));// 移除窗口装饰Atom hints = XInternAtom(display, "_MOTIF_WM_HINTS", False);struct {unsigned long flags;unsigned long functions;unsigned long decorations;long input_mode;unsigned long status;} motif_hints = {2, 0, 0, 0, 0}; // decorations = 0 表示无装饰XChangeProperty(display, window,hints, hints, 32, PropModeReplace,(unsigned char *)&motif_hints, sizeof(motif_hints) / sizeof(long));/* 显示(映射)窗口 */XMapWindow(display, window);XEvent  event;while (1) {XNextEvent(display,&event);}XDestroyWindow(display, window);XCloseDisplay(display);return 0;
}

编译以上程序,运行结果如下,显示了一个不带有标题栏的窗口。

在这里插入图片描述

我们创建了一个不带标题栏的窗口,但是该窗口显示后不能移动也不能通过交互方式(鼠标和按钮)退出。我们让该窗口处理键盘按钮事件,当按下Esc键时,退出程序。实现代码如下

#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <X11/keysymdef.h>
#include <X11/keysym.h>int main() {Display *display;Window window;int screen;/* 打开与X服务器的连接 */display = XOpenDisplay(NULL);if (display == NULL) {fprintf(stderr, "无法打开X显示器\n");exit(1);}screen = DefaultScreen(display);/* 创建窗口 */window = XCreateSimpleWindow(display, RootWindow(display, screen), 10, 10, 400, 300, 1,BlackPixel(display, screen), WhitePixel(display, screen));// 移除窗口装饰Atom hints = XInternAtom(display, "_MOTIF_WM_HINTS", False);struct {unsigned long flags;unsigned long functions;unsigned long decorations;long input_mode;unsigned long status;} motif_hints = {2, 0, 0, 0, 0}; // decorations = 0 表示无装饰XChangeProperty(display, window,hints, hints, 32, PropModeReplace,(unsigned char *)&motif_hints, sizeof(motif_hints) / sizeof(long));XSelectInput(display,window,KeyPressMask|ButtonPressMask);/* 显示(映射)窗口 */XMapWindow(display, window);XEvent  event;while (1) {XNextEvent(display,&event);if (event.type == KeyPress) {KeySym keysym = XLookupKeysym(&event.xkey,0);if (keysym == XK_Escape) {break;}}}XDestroyWindow(display, window);XCloseDisplay(display);return 0;
}

编译以上程序,按下Esc键退出程序。下面我们再为窗口添加鼠标按下时,移动窗口功能。可以使用XSendEvent向窗口发送ClientMessage,以便把窗口交窗口管理器管理实现拖动窗口操作。使用XSendEvent发送ClientMessage的实现代码如下

void StartWindowMoveResize(Display *display, Window window, int x_root, int y_root, int operateCode) {XEvent event;memset(&event, 0, sizeof(event));XUngrabPointer(display,CurrentTime);event.xclient.type = ClientMessage;event.xclient.window = window;event.xclient.message_type = XInternAtom(display, "_NET_WM_MOVERESIZE", False);event.xclient.format = 32;event.xclient.data.l[0] = x_root; // 鼠标指针的根窗口 X 坐标event.xclient.data.l[1] = y_root; // 鼠标指针的根窗口 Y 坐标event.xclient.data.l[2] = operateCode;      // 动作:8 表示移动窗口event.xclient.data.l[3] = Button1; // 使用鼠标左键event.xclient.data.l[4] = 0;      // 保留字段// 发送事件到根窗口XSendEvent(display, DefaultRootWindow(display), False,SubstructureRedirectMask | SubstructureNotifyMask, &event);XFlush(display); // 刷新事件队列
}

在StartWindowMoveResize函数中,需要了解下operateCode,这是一个int值,取值范围为[0,8]。各取值范围及含义如下

OperateCode含义
0左上角方向改变窗口大小
1向上方向改变窗口大小
2右上角方向改变窗口大小
3向右改变窗口大小
4右下角方向改变窗口大小
5向下改变窗口大小
6左下角方向改变窗口大小
7向左改变窗口大小
8用于移动窗口位置

不使用系统自带标题栏,实现窗口的移动完整代码如下

#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <X11/keysymdef.h>
#include <X11/keysym.h>
#include <cstring>void StartWindowMoveResize(Display *display, Window window, int x_root, int y_root, int operateCode) {XEvent event;memset(&event, 0, sizeof(event));XUngrabPointer(display,CurrentTime);event.xclient.type = ClientMessage;event.xclient.window = window;event.xclient.message_type = XInternAtom(display, "_NET_WM_MOVERESIZE", False);event.xclient.format = 32;event.xclient.data.l[0] = x_root; // 鼠标指针的根窗口 X 坐标event.xclient.data.l[1] = y_root; // 鼠标指针的根窗口 Y 坐标event.xclient.data.l[2] = operateCode;      // 动作:8 表示移动窗口event.xclient.data.l[3] = Button1; // 使用鼠标左键event.xclient.data.l[4] = 0;      // 保留字段// 发送事件到根窗口XSendEvent(display, DefaultRootWindow(display), False,SubstructureRedirectMask | SubstructureNotifyMask, &event);XFlush(display); // 刷新事件队列
}int main() {Display *display;Window window;int screen;/* 打开与X服务器的连接 */display = XOpenDisplay(NULL);if (display == NULL) {fprintf(stderr, "无法打开X显示器\n");exit(1);}screen = DefaultScreen(display);/* 创建窗口 */window = XCreateSimpleWindow(display, RootWindow(display, screen), 10, 10, 400, 300, 1,BlackPixel(display, screen), WhitePixel(display, screen));// 移除窗口装饰Atom hints = XInternAtom(display, "_MOTIF_WM_HINTS", False);struct {unsigned long flags;unsigned long functions;unsigned long decorations;long input_mode;unsigned long status;} motif_hints = {2, 0, 0, 0, 0}; // decorations = 0 表示无装饰XChangeProperty(display, window,hints, hints, 32, PropModeReplace,(unsigned char *)&motif_hints, sizeof(motif_hints) / sizeof(long));XSelectInput(display,window,KeyPressMask|ButtonPressMask|PointerMotionMask);/* 显示(映射)窗口 */XMapWindow(display, window);XEvent  event;while (1) {XNextEvent(display,&event);if (event.type == KeyPress) {KeySym keysym = XLookupKeysym(&event.xkey,0);if (keysym == XK_Escape) {break;}}if (event.type == MotionNotify) {printf("Motion State = %d\n",event.xmotion.state);if (event.xmotion.state & Button1Mask) {//鼠标移动并且按下左键,并且按下左键的位置在标题栏处,当然这里的标题栏的高度、宽度都是可以自定义的if (event.xmotion.y <= 50) {StartWindowMoveResize(display,window,event.xmotion.x_root,event.xmotion.y_root,8);}}}}XDestroyWindow(display, window);XCloseDisplay(display);return 0;
}

运行以上程序,在窗口上方50像素内按下鼠标并移动可以实现窗口移动。当然这里我们可以修改代码判断鼠标是否在改变窗口大小的区域内,以实现改变窗口大小功能,这里不再给出实现代码。

之所以在创建异形窗口这里给出代码实现窗口移动,退出功能是因为当我们创建异形时,正常情况下都会创建没有标题栏的窗口(不让窗口管理器对我们创建的窗口进行装饰如标题栏、边框等);否则我们的异形窗口带有标题栏。而这时我们就需要处理窗口退出、移动、改变大小事件。

2.实现一个五角星窗口

我们想要实现一个五角星的窗口,可以使用多边形来实现,示例如下图所示

在这里插入图片描述

在上面所示的五角星示例中,使用笛卡尔坐标系来描述五角星,两线虚线相交处为坐标原点水平方向从左向右为x轴,垂直方向从下向上为y轴。总共需要10个顶点来绘制五角星,第一个顶点需要落在y轴正坐标上,从标点1、3、5、7、9五个点平分整个圆。任意两个点之间的角度为360/5=72度。由于点1需要在y轴上,点1的x坐标为0,y坐标为radius * sin(90.0)。假设Radius为五角星所在圆的半径。点3的x坐标为radius * cos(162.0);所在y轴的坐标为radius*sin(162)。这里的162=90+72。点5,点7,点9的坐标以此类推。对于2,4,6,8,10这5个点的坐标,我们来看点2,这个点是由点1和点5组成的直线与点3与点9组成的直线,两条直线的交点形成,我们只需要解两条直线的交点,即可算出点2的坐标。其它4个点以此类推。

坐标映射

在上面的示例中,我们所有的点都是在笛卡尔坐标系下计算完成的。需要把这些点映射到屏幕中窗口中。

笛卡尔坐标系示意图如下:

在这里插入图片描述

屏幕坐标系示意图如下

在这里插入图片描述

从上面两个图中我们可以知道在窗口界面中坐标x方向从0开始向右增,y方向从0开始向下增长。而在笛卡尔坐标系中,x从左向右增长,并且可能存在负数值;y轴方向从下向上增长。所以当我们把笛卡尔坐标系下原点(0,0)映射到屏幕坐标系的( x,y)时。 x轴方向所有的点加上x即可将笛卡尔坐标系坐标映射到屏幕坐标。而对于y轴方向,在笛卡尔坐标系下y由下向上增长,在屏幕坐标系下,y轴坐标值由上向下增长,这时我们需要把在笛卡尔坐标系下得到y轴坐标值乘以(-1);负数是改变方向,再把乘以-1的结果加y。根据以上规则对于笛卡尔坐标系下的坐标(x1,y1),假设屏幕坐标中心点为(x,y)映射到屏幕坐标系下的坐标(x2,y2)存在以下关系

x 2 = x + x 1 y 2 = y + y 1 ∗ ( − 1 ) x2 = x + x1 \\ y2 = y + y1*(-1) x2=x+x1y2=y+y1(1)

以上映射关系使用矩阵可表示为

( x 2 y 2 1 ) = ( 1 0 x 0 − 1 y 0 0 1 ) ( x 1 y 1 1 ) \begin{pmatrix} x2 \\ y2 \\ 1 \end{pmatrix} = \begin{pmatrix} 1 & 0 & x \\ 0 & -1 & y \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} x1 \\ y1 \\ 1 \end{pmatrix} x2y21 = 100010xy1 x1y11

有了以上数学基础,我们可以编码实现10个点坐标计算。实现代码如下

inline int CalculateIntersectX(XPoint point1,XPoint point2, XPoint point3,XPoint point4) {double k1 = (point2.y-point1.y)*1.0/(point2.x-point1.x);double k2 = (point4.y-point3.y)*1.0/(point4.x-point3.x);return (point3.y-k2*point3.x + k1*point1.x -point1.y)/(k1-k2);
}inline int CalculateIntersectY(XPoint point1,XPoint point2,int x) {double k = (point2.y-point1.y)*1.0/(point2.x-point1.x);return k*x + point1.y -k*point1.x;
}Region CreateFiveStarRegion(int x,int y,int radius) {XPoint  points[10] = {0};points[0].x = x + radius * cos(90.0*(M_PI/180.0));points[0].y = y - radius * sin(90.0*(M_PI/180.0));points[2].x = x + radius * cos(162.0*(M_PI/180.0));points[2].y = y - radius * sin(162.0*(M_PI/180.0));points[4].x = x + radius * cos(234.0*(M_PI/180.0));points[4].y = y - radius * sin(234.0*(M_PI/180.0));points[6].x = x + radius * cos(306.0*(M_PI/180.0));points[6].y = y - radius * sin(306.0*(M_PI/180.0));points[8].x = x + radius * cos(18.0*(M_PI/180.0));points[8].y = y - radius * sin(18.0*(M_PI/180.0));points[1].x = CalculateIntersectX(points[0],points[4],points[2],points[8]);points[1].y = CalculateIntersectY(points[0],points[4],points[1].x);points[3].x = CalculateIntersectX(points[0],points[4],points[2],points[6]);points[3].y = CalculateIntersectY(points[0],points[4],points[3].x);points[5].x = CalculateIntersectX(points[2],points[6],points[4],points[8]);points[5].y = CalculateIntersectY(points[2],points[6],points[5].x);points[7].x = CalculateIntersectX(points[4],points[8],points[0],points[6]);points[7].y = CalculateIntersectY(points[4],points[8],points[7].x);points[9].x = CalculateIntersectX(points[0],points[6],points[2],points[8]);points[9].y = CalculateIntersectY(points[0],points[6],points[9].x);
}

利用这些点我们可以创建一个多边形区域,并实现一个五角星异形窗口。完整代码如下

#include <X11/Xlib.h>
#include <X11/extensions/shape.h>
#include <X11/extensions/Xrender.h>
#include <stdio.h>
#include <string.h>
#include <chrono>
#include <iostream>
#include <cmath>using namespace std;inline int CalculateIntersectX(XPoint point1,XPoint point2, XPoint point3,XPoint point4) {double k1 = (point2.y-point1.y)*1.0/(point2.x-point1.x);double k2 = (point4.y-point3.y)*1.0/(point4.x-point3.x);return (point3.y-k2*point3.x + k1*point1.x -point1.y)/(k1-k2);
}inline int CalculateIntersectY(XPoint point1,XPoint point2,int x) {double k = (point2.y-point1.y)*1.0/(point2.x-point1.x);return k*x + point1.y -k*point1.x;
}Region CreateFiveStarRegion(int x,int y,int radius) {XPoint  points[10] = {0};points[0].x = x + radius * cos(90.0*(M_PI/180.0));points[0].y = y - radius * sin(90.0*(M_PI/180.0));points[2].x = x + radius * cos(162.0*(M_PI/180.0));points[2].y = y - radius * sin(162.0*(M_PI/180.0));points[4].x = x + radius * cos(234.0*(M_PI/180.0));points[4].y = y - radius * sin(234.0*(M_PI/180.0));points[6].x = x + radius * cos(306.0*(M_PI/180.0));points[6].y = y - radius * sin(306.0*(M_PI/180.0));points[8].x = x + radius * cos(18.0*(M_PI/180.0));points[8].y = y - radius * sin(18.0*(M_PI/180.0));points[1].x = CalculateIntersectX(points[0],points[4],points[2],points[8]);points[1].y = CalculateIntersectY(points[0],points[4],points[1].x);points[3].x = CalculateIntersectX(points[0],points[4],points[2],points[6]);points[3].y = CalculateIntersectY(points[0],points[4],points[3].x);points[5].x = CalculateIntersectX(points[2],points[6],points[4],points[8]);points[5].y = CalculateIntersectY(points[2],points[6],points[5].x);points[7].x = CalculateIntersectX(points[4],points[8],points[0],points[6]);points[7].y = CalculateIntersectY(points[4],points[8],points[7].x);points[9].x = CalculateIntersectX(points[0],points[6],points[2],points[8]);points[9].y = CalculateIntersectY(points[0],points[6],points[9].x);Region region = XPolygonRegion(points,10,EvenOddRule);return region;
}// 发送 _NET_WM_MOVERESIZE 消息以启动窗口移动
void start_window_move_resize(Display *display, Window window, int x_root, int y_root, int operateCode) {XEvent event;memset(&event, 0, sizeof(event));XUngrabPointer(display,CurrentTime);event.xclient.type = ClientMessage;event.xclient.window = window;event.xclient.message_type = XInternAtom(display, "_NET_WM_MOVERESIZE", False);event.xclient.format = 32;event.xclient.data.l[0] = x_root; // 鼠标指针的根窗口 X 坐标event.xclient.data.l[1] = y_root; // 鼠标指针的根窗口 Y 坐标event.xclient.data.l[2] = operateCode;      // 动作:8 表示移动窗口event.xclient.data.l[3] = Button1; // 使用鼠标左键event.xclient.data.l[4] = 0;      // 保留字段// 发送事件到根窗口XSendEvent(display, DefaultRootWindow(display), False,SubstructureRedirectMask | SubstructureNotifyMask, &event);XFlush(display); // 刷新事件队列
}int main() {// 打开与 X 服务器的连接Display *display = XOpenDisplay(NULL);if (display == NULL) {fprintf(stderr, "无法连接到 X 服务器\n");return 1;}int screen = DefaultScreen(display);int width = 200;int height = 200;// 创建无装饰窗口Window window = XCreateSimpleWindow(display, RootWindow(display, screen),100, 100, width, height, 1,BlackPixel(display, screen),WhitePixel(display, screen));// 移除窗口装饰Atom hints = XInternAtom(display, "_MOTIF_WM_HINTS", False);struct {unsigned long flags;unsigned long functions;unsigned long decorations;long input_mode;unsigned long status;} motif_hints = {2, 0, 0, 0, 0}; // decorations = 0 表示无装饰XChangeProperty(display, window,hints, hints, 32, PropModeReplace,(unsigned char *)&motif_hints, sizeof(motif_hints) / sizeof(long));// 注册事件XSelectInput(display, window, ExposureMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask);// 显示窗口XMapWindow(display, window);// 主事件循环XEvent event;bool continueRunning = true;while (continueRunning) {XNextEvent(display, &event);if(event.type == Expose){if(event.xexpose.count == 0){printf("窗口被重绘\n");}int edge = width<height?width:height;Region region = CreateFiveStarRegion(edge/2,edge/2,edge/2);// 先测试剪切4个角,看效果XShapeCombineRegion(display, window, ShapeBounding, 0, 0, region, ShapeSet);XDestroyRegion(region);}else if(event.type == MotionNotify) {if (event.xmotion.state & Button1Mask) { // 鼠标左键按下start_window_move_resize(display,window,event.xmotion.x_root,event.xmotion.y_root,8);}}else if(event.type == KeyPress) {KeySym key = XLookupKeysym(&event.xkey,0);if(key == XK_Escape){continueRunning = false;}}}// 清理资源XDestroyWindow(display, window);XCloseDisplay(display);return 0;
}

编译以上代码,运行结果如下:

在这里插入图片描述

以上我们创建了一个五角星的窗口。并且可以使用鼠标拖动、移动该窗口。按Esc键退出程序

3.实现一个圆形窗口

我们可以利用前面通义大模型生成的抗锯齿代码来生成一个圆形区域,结合XShapeCombineRegion实现一个圆形窗口。实现代码如下

#include <X11/Xlib.h>
#include <X11/extensions/shape.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <vector>
#include <algorithm>
#include <iostream>using namespace std;enum CircleCorner{TopLeft,TopRight,BottomRight,BottomLeft
};static bool IsPointExistsInVector(vector<XPoint> &points, XPoint point){return std::find_if(points.begin(),points.end(),[&](const XPoint &iter){return iter.x == point.x && iter.y == point.y;}) != points.end();
}#define DISTANCE() \sqrt(((px)-(cx))*((px)-(cx)) + ((py)-(cy))*((py)-(cy)))#define TopLeft1()                         \do{                                             \px =    (cx) - (x) + (i);                        \py =    (cy) - (y) + (j);                        \point.x = px;                              \point.y = py;                              \double dist = DISTANCE(); \coverage = fmax(0, fmin(1, 0.5 - fabs(dist - r)));\}while(0);#define TopLeft2()                         \do{                                             \px =       (cx) - (y) + (i);                    \py =       (cy) - (x) + (j);                    \point.x  = px;                            \point.y =  py;                            \double dist = DISTANCE();      \coverage = fmax(0, fmin(1, 0.5 - fabs(dist - r))); \}while(0);//{cx + x + i, cy - y + j}, // 右-上-下
//                        {cx + y + i, cy - x + j}, // 右-上-上#define TopRight1()                    \do{                                         \px = (cx) + (x) + (i);                \py = (cy) - (y) + (j);                \point.x = px;                         \point.y = py;                         \double dist = DISTANCE();  \coverage = fmax(0, fmin(1, 0.5 - fabs(dist - r)));\}while(0)#define TopRight2()                            \do{                                                 \px = cx+y+i;                                   \py = cy-x+j;                                   \point.x = px;                                  \point.y = py;                                      \double dist = DISTANCE();               \coverage = fmax(0, fmin(1, 0.5 - fabs(dist - r))); \}while(0)//{cx + x + i, cy + y + j}, // 右-下-下
//                        {cx + y + i, cy + x + j}, // 右-下-上#define BottomRight1()              \do{                         \px = cx+x+i;            \py = cy+y+j;            \point.x = px;              \point.y = py;              \double dist = DISTANCE(); \coverage = fmax(0, fmin(1, 0.5 - fabs(dist - r)));\}while(0)#define BottomRight2()              \do{                         \px = cx + y + i;         \py = cy + x + j;         \point.x = px;              \point.y = py;              \double dist = DISTANCE(); \coverage = fmax(0, fmin(1, 0.5 - fabs(dist - r)));\}while(0)//{cx - x + i, cy + y + j}, // 左-下-下
//                        {cx - y + i, cy + x + j} // 左-下-上#define BottomLeft1() \do{                         \px = cx -x + i;         \py = cy + y +j;         \point.x = px;              \point.y = py;              \double dist = DISTANCE(); \coverage = fmax(0, fmin(1, 0.5 - fabs(dist - r)));\}while(0)#define BottomLeft2()           \do{                         \px = cx -y +i;         \py = cy + x +j;         \point.x = px;              \point.y = py;              \double dist = DISTANCE(); \coverage = fmax(0, fmin(1, 0.5 - fabs(dist - r)));\}while(0)   \
#define CONCAT(x,y) x ## y#define CreateCorner(CornerType) \CONCAT(CornerType,1)();    \if(coverage>=0.30 && (!IsPointExistsInVector(circlePoints,point))){ \circlePoints.push_back(point);                    \}                                                                      \CONCAT(CornerType,2)();                                               \if(coverage>=0.30 && (!IsPointExistsInVector(circlePoints,point))){   \circlePoints.push_back(point);                                    \}void GetRoundCornerPoints(int cx, int cy, int r, CircleCorner cornerType, vector<XPoint> &circlePoints){int x = 0, y = r;int d = 1 - r;int deltaE = 3, deltaSE = -2 * r + 5;while (x <= y) {// 处理当前点及其对称点for (int i = -1; i <= 1; i++) {for (int j = -1; j <= 1; j++) {int px,py;XPoint point;double coverage = 0.0;if(cornerType == TopLeft){CreateCorner(TopLeft);}else if(cornerType == TopRight){CreateCorner(TopRight);}else if(cornerType == BottomRight){CreateCorner(BottomRight);}else if(cornerType == BottomLeft){CreateCorner(BottomLeft);}}}// 更新决策变量if (d < 0) {d += deltaE;deltaE += 2;deltaSE += 2;} else {d += deltaSE;deltaE += 2;deltaSE += 4;y--;}x++;}
}static Region CreateCircleRegion(int cx, int cy, int r) {vector<XPoint>   topLeftPoints;vector<XPoint>   topRightPoints;vector<XPoint>   bottomRightPoints;vector<XPoint>   bottomLeftPoints;GetRoundCornerPoints(cx,cy, r, TopLeft, topLeftPoints);GetRoundCornerPoints(cx,cy, r, TopRight, topRightPoints);GetRoundCornerPoints(cx,cy, r, BottomRight,  bottomRightPoints);GetRoundCornerPoints(cx,cy, r, BottomLeft, bottomLeftPoints);//左上角的四分之一圆,点按照x轴增大,y轴增大的方式排序std::sort(topLeftPoints.begin(),topLeftPoints.end(),[&](const XPoint &a,const XPoint &b){if(a.x == b.x){return a.y < b.y;}return a.x < b.x;});//右上角四分之一圆点,按照x轴增大,y轴减少的方式排序。std::sort(topRightPoints.begin(),topRightPoints.end(),[&](const XPoint &a, const XPoint &b){if(a.x == b.x){return a.y > b.y;}return a.x < b.x;});//右下角四分之上圆点,按照x轴减小,y轴增大方式排序std::sort(bottomRightPoints.begin(),bottomRightPoints.end(),[&](const XPoint &a, const XPoint &b){if(a.x == b.x){return a.y < b.y;}return a.x > b.x;});//左下角四分之一圆点,按照x轴减少,y轴减少排序std::sort(bottomLeftPoints.begin(), bottomLeftPoints.end(), [&](const XPoint &a, const XPoint &b){if(a.x == b.x){return a.y > b.y;}return a.x > b.x;});vector<XPoint> circlePoints;circlePoints.reserve(topLeftPoints.size() + topRightPoints.size() + bottomRightPoints.size() + bottomLeftPoints.size());std::copy(topLeftPoints.begin(),topLeftPoints.end(),std::back_inserter(circlePoints));std::copy(topRightPoints.begin(),topRightPoints.end(),std::back_inserter(circlePoints));std::copy(bottomRightPoints.begin(), bottomRightPoints.end(), std::back_inserter(circlePoints));std::copy(bottomLeftPoints.begin(), bottomLeftPoints.end(), std::back_inserter(circlePoints));return XPolygonRegion(circlePoints.data(), circlePoints.size(), EvenOddRule);
}int main() {Display *display;Window window;int screen;display = XOpenDisplay(NULL);if (display == NULL) {fprintf(stderr, "无法打开X显示\n");exit(1);}screen = DefaultScreen(display);window = XCreateSimpleWindow(display, RootWindow(display, screen), 100, 100, 400, 400, 1,BlackPixel(display, screen), WhitePixel(display, screen));XSelectInput(display,window,KeyPressMask|PointerMotionMask|ButtonPressMask);// 显示窗口XMapWindow(display, window);// 移除窗口装饰Atom hints = XInternAtom(display, "_MOTIF_WM_HINTS", False);struct {unsigned long flags;unsigned long functions;unsigned long decorations;long input_mode;unsigned long status;} motif_hints = {2, 0, 0, 0, 0}; // decorations = 0 表示无装饰XChangeProperty(display, window,hints, hints, 32, PropModeReplace,(unsigned char *)&motif_hints, sizeof(motif_hints) / sizeof(long));// 创建并应用圆形区域Region circleRegion = CreateCircleRegion(200,200,150);XRectangle rect = {0,0,400,400};Region region = XCreateRegion();XUnionRectWithRegion(&rect, region, region);XIntersectRegion(region, circleRegion, region);XDestroyRegion(circleRegion);// 将圆形区域应用为窗口形状XShapeCombineRegion(display, window, ShapeBounding, 0, 0, region, ShapeSet);XDestroyRegion(region);XMoveWindow(display,window,300,500);while (1) {XEvent event;XNextEvent(display,&event);if (event.type == KeyPress) {KeySym keysym = XLookupKeysym(&event.xkey,0);if (keysym == XK_Escape) {break;}}}XDestroyWindow(display,window);// 清理资源XCloseDisplay(display);return 0;
}

编译以上代码,运行结果如下

在这里插入图片描述

4.圆形渐变窗口

在上面我们实现了一个圆形窗口,可以使用XRender扩展实现一个圆形的渐变窗口。关于xlib实现渐变可以查看之前的实现代码。

实现代码如下

#include <X11/Xlib.h>
#include <X11/extensions/shape.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <vector>
#include <algorithm>
#include <iostream>
#include <X11/extensions/render.h>
#include <X11/extensions/Xrender.h>using namespace std;enum CircleCorner{TopLeft,TopRight,BottomRight,BottomLeft
};static bool IsPointExistsInVector(vector<XPoint> &points, XPoint point){return std::find_if(points.begin(),points.end(),[&](const XPoint &iter){return iter.x == point.x && iter.y == point.y;}) != points.end();
}#define DISTANCE() \sqrt(((px)-(cx))*((px)-(cx)) + ((py)-(cy))*((py)-(cy)))#define TopLeft1()                         \do{                                             \px =    (cx) - (x) + (i);                        \py =    (cy) - (y) + (j);                        \point.x = px;                              \point.y = py;                              \double dist = DISTANCE(); \coverage = fmax(0, fmin(1, 0.5 - fabs(dist - r)));\}while(0);#define TopLeft2()                         \do{                                             \px =       (cx) - (y) + (i);                    \py =       (cy) - (x) + (j);                    \point.x  = px;                            \point.y =  py;                            \double dist = DISTANCE();      \coverage = fmax(0, fmin(1, 0.5 - fabs(dist - r))); \}while(0);//{cx + x + i, cy - y + j}, // 右-上-下
//                        {cx + y + i, cy - x + j}, // 右-上-上#define TopRight1()                    \do{                                         \px = (cx) + (x) + (i);                \py = (cy) - (y) + (j);                \point.x = px;                         \point.y = py;                         \double dist = DISTANCE();  \coverage = fmax(0, fmin(1, 0.5 - fabs(dist - r)));\}while(0)#define TopRight2()                            \do{                                                 \px = cx+y+i;                                   \py = cy-x+j;                                   \point.x = px;                                  \point.y = py;                                      \double dist = DISTANCE();               \coverage = fmax(0, fmin(1, 0.5 - fabs(dist - r))); \}while(0)//{cx + x + i, cy + y + j}, // 右-下-下
//                        {cx + y + i, cy + x + j}, // 右-下-上#define BottomRight1()              \do{                         \px = cx+x+i;            \py = cy+y+j;            \point.x = px;              \point.y = py;              \double dist = DISTANCE(); \coverage = fmax(0, fmin(1, 0.5 - fabs(dist - r)));\}while(0)#define BottomRight2()              \do{                         \px = cx + y + i;         \py = cy + x + j;         \point.x = px;              \point.y = py;              \double dist = DISTANCE(); \coverage = fmax(0, fmin(1, 0.5 - fabs(dist - r)));\}while(0)//{cx - x + i, cy + y + j}, // 左-下-下
//                        {cx - y + i, cy + x + j} // 左-下-上#define BottomLeft1() \do{                         \px = cx -x + i;         \py = cy + y +j;         \point.x = px;              \point.y = py;              \double dist = DISTANCE(); \coverage = fmax(0, fmin(1, 0.5 - fabs(dist - r)));\}while(0)#define BottomLeft2()           \do{                         \px = cx -y +i;         \py = cy + x +j;         \point.x = px;              \point.y = py;              \double dist = DISTANCE(); \coverage = fmax(0, fmin(1, 0.5 - fabs(dist - r)));\}while(0)   \
#define CONCAT(x,y) x ## y#define CreateCorner(CornerType) \CONCAT(CornerType,1)();    \if(coverage>=0.30 && (!IsPointExistsInVector(circlePoints,point))){ \circlePoints.push_back(point);                    \}                                                                      \CONCAT(CornerType,2)();                                               \if(coverage>=0.30 && (!IsPointExistsInVector(circlePoints,point))){   \circlePoints.push_back(point);                                    \}void GetRoundCornerPoints(int cx, int cy, int r, CircleCorner cornerType, vector<XPoint> &circlePoints){int x = 0, y = r;int d = 1 - r;int deltaE = 3, deltaSE = -2 * r + 5;while (x <= y) {// 处理当前点及其对称点for (int i = -1; i <= 1; i++) {for (int j = -1; j <= 1; j++) {int px,py;XPoint point;double coverage = 0.0;if(cornerType == TopLeft){CreateCorner(TopLeft);}else if(cornerType == TopRight){CreateCorner(TopRight);}else if(cornerType == BottomRight){CreateCorner(BottomRight);}else if(cornerType == BottomLeft){CreateCorner(BottomLeft);}}}// 更新决策变量if (d < 0) {d += deltaE;deltaE += 2;deltaSE += 2;} else {d += deltaSE;deltaE += 2;deltaSE += 4;y--;}x++;}
}static Region CreateCircleRegion(int cx, int cy, int r) {vector<XPoint>   topLeftPoints;vector<XPoint>   topRightPoints;vector<XPoint>   bottomRightPoints;vector<XPoint>   bottomLeftPoints;GetRoundCornerPoints(cx,cy, r, TopLeft, topLeftPoints);GetRoundCornerPoints(cx,cy, r, TopRight, topRightPoints);GetRoundCornerPoints(cx,cy, r, BottomRight,  bottomRightPoints);GetRoundCornerPoints(cx,cy, r, BottomLeft, bottomLeftPoints);//左上角的四分之一圆,点按照x轴增大,y轴增大的方式排序std::sort(topLeftPoints.begin(),topLeftPoints.end(),[&](const XPoint &a,const XPoint &b){if(a.x == b.x){return a.y < b.y;}return a.x < b.x;});//右上角四分之一圆点,按照x轴增大,y轴减少的方式排序。std::sort(topRightPoints.begin(),topRightPoints.end(),[&](const XPoint &a, const XPoint &b){if(a.x == b.x){return a.y > b.y;}return a.x < b.x;});//右下角四分之上圆点,按照x轴减小,y轴增大方式排序std::sort(bottomRightPoints.begin(),bottomRightPoints.end(),[&](const XPoint &a, const XPoint &b){if(a.x == b.x){return a.y < b.y;}return a.x > b.x;});//左下角四分之一圆点,按照x轴减少,y轴减少排序std::sort(bottomLeftPoints.begin(), bottomLeftPoints.end(), [&](const XPoint &a, const XPoint &b){if(a.x == b.x){return a.y > b.y;}return a.x > b.x;});vector<XPoint> circlePoints;circlePoints.reserve(topLeftPoints.size() + topRightPoints.size() + bottomRightPoints.size() + bottomLeftPoints.size());std::copy(topLeftPoints.begin(),topLeftPoints.end(),std::back_inserter(circlePoints));std::copy(topRightPoints.begin(),topRightPoints.end(),std::back_inserter(circlePoints));std::copy(bottomRightPoints.begin(), bottomRightPoints.end(), std::back_inserter(circlePoints));std::copy(bottomLeftPoints.begin(), bottomLeftPoints.end(), std::back_inserter(circlePoints));return XPolygonRegion(circlePoints.data(), circlePoints.size(), EvenOddRule);
}int main() {Display *display;Window window;int screen;display = XOpenDisplay(NULL);if (display == NULL) {fprintf(stderr, "无法打开X显示\n");exit(1);}screen = DefaultScreen(display);window = XCreateSimpleWindow(display, RootWindow(display, screen), 100, 100, 400, 400, 1,BlackPixel(display, screen), WhitePixel(display, screen));XSelectInput(display,window,ExposureMask | KeyPressMask|PointerMotionMask|ButtonPressMask);// 显示窗口XMapWindow(display, window);// 移除窗口装饰Atom hints = XInternAtom(display, "_MOTIF_WM_HINTS", False);struct {unsigned long flags;unsigned long functions;unsigned long decorations;long input_mode;unsigned long status;} motif_hints = {2, 0, 0, 0, 0}; // decorations = 0 表示无装饰XChangeProperty(display, window,hints, hints, 32, PropModeReplace,(unsigned char *)&motif_hints, sizeof(motif_hints) / sizeof(long));XMoveWindow(display,window,300,500);while (1) {XEvent event;XNextEvent(display,&event);if (event.type == Expose) {// 创建并应用圆形区域Region circleRegion = CreateCircleRegion(200,200,150);XRectangle rect = {0,0,400,400};Region region = XCreateRegion();XUnionRectWithRegion(&rect, region, region);XIntersectRegion(region, circleRegion, region);XDestroyRegion(circleRegion);// 将圆形区域应用为窗口形状XShapeCombineRegion(display, window, ShapeBounding, 0, 0, region, ShapeSet);XDestroyRegion(region);// 创建窗口的图片(Picture)Picture window_picture = XRenderCreatePicture(display, window,XRenderFindVisualFormat(display, DefaultVisual(display, screen)), 0,NULL);// 定义线性渐变的起点和终点XLinearGradient gradient;gradient.p1.x = XDoubleToFixed(0); // 起点 x 坐标gradient.p1.y = XDoubleToFixed(0); // 起点 y 坐标gradient.p2.x = XDoubleToFixed(0); // 终点 x 坐标gradient.p2.y = XDoubleToFixed(400); // 终点 y 坐标// 定义渐变的颜色停止点XFixed stops[3];XRenderColor colors[3];// 第一个颜色:红stops[0] = XDoubleToFixed(0.0);colors[0].red = 65535;colors[0].green = 0;colors[0].blue = 0;colors[0].alpha = 65535; // 不透明stops[1] = XDoubleToFixed(0.5);colors[1].red = 0;colors[1].green = 0;colors[1].blue = 65535;colors[1].alpha = 65535;// 第三个颜色:绿stops[2] = XDoubleToFixed(1.0);colors[2].red = 0; // 红色 (范围 0-65535)colors[2].green = 65535;colors[2].blue = 0;colors[2].alpha = 65535; // 不透明// 创建线性渐变图案Picture gradient_picture = XRenderCreateLinearGradient(display, &gradient, stops, colors, 3);XRenderComposite(display, PictOpSrc, gradient_picture, None, window_picture,0, 0, 0, 0, 0, 0, 400, 400);XRenderFreePicture(display, gradient_picture);XRenderFreePicture(display, window_picture);}if (event.type == KeyPress) {KeySym keysym = XLookupKeysym(&event.xkey,0);if (keysym == XK_Escape) {break;}}}XDestroyWindow(display,window);// 清理资源XCloseDisplay(display);return 0;
}

编译以上程序运行结果如下

在这里插入图片描述

利用xlib提供的创建多边形区域,我们可以复杂高级的界面开状,同时结合XRender扩展,我们可以高级炫丽的界面效果。满足不同项目对于对界面渲染的需求。

相关文章:

  • Flask 与 Django 服务器部署
  • Django 项目中,将所有数据表注册到 Django 后台管理系统
  • C++(24):容器类<list>
  • 学习源码?
  • cmd里可以使用npm,vscode里使用npm 报错
  • OpenCv(7.0)——银行卡号识别
  • 中山大学具身智能体高效探索与精准问答!Beyond the Destination:面向探索感知的具身问答新基准
  • std::ranges::views::stride 和 std::ranges::stride_view
  • 2025年AI与网络安全的终极博弈:冲击、重构与生存法则
  • 【OpenCV基础2】
  • Interrupt 2025 大会回顾:关于LangChain 的 AI Agent会议内容总结
  • 如何提高嵌入式软件设计的代码质量
  • 用 CodeBuddy 实现「IdeaSpark 每日灵感卡」:一场 UI 与灵感的极简之旅
  • MathType公式如何按照(1)(2)…编号
  • PYTHON训练营DAY30
  • Windows多功能工具箱软件推荐
  • Linux配置SSH密钥认证
  • 2025年新发布的 基于鸿蒙操作系统5的 电脑可以支持Windows 应用嘛?
  • android13以太网静态ip不断断开连上问题
  • 什么业务需要用到waf
  • 中国首次当选联合国教科文组织1970年《公约》缔约国大会主席国
  • 2025年“新时代网络文明公益广告”征集展示活动在沪启动
  • 国防部:中方愿与俄方不断增强两军关系良好发展势头
  • 德国总理默茨发表首份政府声明:将提升国防能力,全力发展经济
  • 京东CEO许冉:外卖日单量接近2000万单,看到外卖对平台拉动和转化效应
  • 俄官员说将适时宣布与乌克兰谈判代表