系列文章目录
本系列文章记录在Linux操作系统下,如何在不依赖QT、GTK等开源GUI库的情况下,基于x11窗口系统(xlib)图形界面应用程序开发。之所以使用x11进行窗口开发,是在开发一个基于duilib跨平台的界面库项目,使用gtk时,发现基于gtk的开发,依赖的动态库太多,发布时太麻烦,gtk不支持静态库编译发布。所以我们决定使用更底层的xlib接口。在此记录下linux系统下基于xlib接口的界面开发
一、xlib创建窗口
二、xlib事件
三、xlib窗口图元
四、xlib区域
五、xlib绘制按钮控件
六、绘制图片
七、xlib窗口渲染
八、实现编辑框控件
九、异形窗口
十、基于xlib实现定时器
十一、xlib绘制编辑框-续
十二、Linux实现截屏小工具
当xlib窗口系统成功创建一个窗体后,我们可以在Expose事件中向窗口放入一些元素展示给用户。xlib提供了可以向窗口中放入文本、点、线、矩形、位图等元素,这些基本的元素我们可以称之为图元。这篇文章记录如何在xlib窗口中绘制文本、改变文本字体颜色;在窗口中绘制矩形,弧形,点,线等元素,能够改变线条的样式如线条颜色、线条宽度等属性。
1.绘制文本
在xlib窗口系统中,可以使用XDrawString将文本信息绘制到窗口中。以下是一个简单的示例程序
#include <clocale>
#include <cstring>
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
void DoPaint(Display *display, Window window) {
char drawText[] = "Draw Text...";
GC gc = XCreateGC(display,window,0,nullptr);
XDrawString(display,window,gc,30,50,drawText,strlen(drawText));
XFreeGC(display,gc);
}
int main() {
Display *display;
Window window;
int screen;
XEvent event;
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));
/* 选择要接收的事件类型 */
XSelectInput(display, window, ExposureMask);
XMapWindow(display, window);
while (1) {
XNextEvent(display, &event);
if (event.type == Expose) {
DoPaint(display,window);
}
}
XDestroyWindow(display, window);
XCloseDisplay(display);
return 0;
}
DoPaint由窗口的Expose事件触发时调用。在DoPaint函数中调用XCreateGC和XDrawString进行文本绘制。XCreateGC返回类型是GC,这是一个图形上下文的句柄标识,我们可以通过对GC进行设置,来改变显示到窗口的文字的颜色,线条的样式。XDrawString(display,window,gc,30,50,drawText,strlen(drawText));该行代码表示将距离窗口左上角向右偏移30像素,向下偏移50像素的位置做为文本drawText起始位置,将drawText中存的文本绘制到窗口中。
编译以上程序,运行效果如下:

我们可以使用XSetForeground对GC进行设置,来改变显示文字的颜色,修改后的DoPaint如下
void DoPaint(Display *display, Window window) {
char drawText[] = "Draw Text...";
GC gc = XCreateGC(display,window,0,nullptr);
XSetForeground(display,gc,0xff0000);
XDrawString(display,window,gc,30,50,drawText,strlen(drawText));
XFreeGC(display,gc);
}
XSetForeground的第三个参数是一个unsigned long类型,实际有作用也就是低24位,分别用8位表示红、绿、蓝颜色分量。以上我们设置了红色,编译运行后,窗口显示如下

在这篇文章的后续示例代码中,我们不再贴出所有的代码,所有的绘制操作都是在DoPaint函数中完成。
在中文开发环境中,我们希望绘制一些中文字符到屏幕。如下的DoPaint源码所示
void DoPaint(Display *display, Window window) {
setlocale(LC_ALL, ""); // 设置合适的locale环境
XSetLocaleModifiers(""); // 可能需要调整locale修饰符
char drawText[] = "Draw Text...";
char utf8String[] = "汉字文本...";
GC gc = XCreateGC(display,window,0,nullptr);
XDrawString(display,window,gc,30,50,drawText,strlen(drawText));
XDrawString(display,window,gc,30,80,utf8String,strlen(utf8String));
XFreeGC(display,gc);
}
很不幸,以上程序并没有得到我们想要的结果,而是得到一串乱码。如下

原因是XDrawString 函数默认处理的是 Latin-1 编码的文本,并且它可能没有正确地处理 UTF-8 编码的汉字文本。我们可以使用Xutf8DrawString来绘制文本或是Xft库来进行汉字文本的绘制。由于Xft库支持freetype字体,并且提供更好的渲染效果(如抗锯齿),同时像pango这样的文本处理库也是使用xft进行文本渲染。在测试过程中使用Xuft8DrawString有可能是某些函数没有正确调用,测试中并没有看到正确的汉字文本输出到窗口。
2.绘制中文
我们使用Xft库来完成汉字文本的绘制。代码如下:
#include <X11/Xft/Xft.h>
void DoPaint(Display *display, Window window) {
char drawText[] = "Draw Text...";
char utf8String[] = "汉字文本...";
int screen = DefaultScreen(display);
XftColor xftColor;
xftColor.color.alpha=0xffff;
xftColor.color.red = 0;
xftColor.color.green = 0;
xftColor.color.blue = 0;
XftFont *font = XftFontOpenName(display, screen, "文泉驿微米黑-20");
XftDraw *xftDraw = XftDrawCreate(display,window,DefaultVisual(display,screen),DefaultColormap(display,screen));
XftDrawStringUtf8(xftDraw,&xftColor,font,30,50,reinterpret_cast<const FcChar8*>(drawText),strlen(drawText));
XftDrawStringUtf8(xftDraw,&xftColor,font,30,120,reinterpret_cast<const FcChar8*>(utf8String),strlen(utf8String));
XftFontClose(display,font);
XftDrawDestroy(xftDraw);
}
想要编译运行以上程序,我们需要系统中安装了Xft的开发库。在ubuntu系统下可以使用如下命令安装Xft开发库。
sudo apt install libxft-dev
在编译代码的时候我们还需要链接上Xft的的动态库,否则会提示XftDrawStringUtf8等函数实现找不到。
g++ xxx.cpp -lX11 -lXft
以上代码编译运行后,界面显示如下:

使用Xft库我们可以达到很好文本渲染效果,如我们绘制一个红色透明度为50%的文本,代码如下
void DoPaint(Display *display, Window window) {
char drawText[] = "Draw Text...";
char utf8String[] = "汉字文本...";
int screen = DefaultScreen(display);
XftColor xftColor;
xftColor.color.alpha=0x8000;
xftColor.color.red = 0xffff;
xftColor.color.green = 0;
xftColor.color.blue = 0;
XftFont *font = XftFontOpenName(display, screen, "文泉驿微米黑-20");
XftDraw *xftDraw = XftDrawCreate(display,window,DefaultVisual(display,screen),DefaultColormap(display,screen));
XftDrawStringUtf8(xftDraw,&xftColor,font,30,50,reinterpret_cast<const FcChar8*>(drawText),strlen(drawText));
XftDrawStringUtf8(xftDraw,&xftColor,font,30,120,reinterpret_cast<const FcChar8*>(utf8String),strlen(utf8String));
XftFontClose(display,font);
XftDrawDestroy(xftDraw);
}
以上代码我们对XftColor结构体字段进行了设置,在XftColor结构体,每个字段的取值并不是我们使用32位argb表示时每个分量取值范围在[0-255]之间,在XftColor中alpha、red、green、blue都被unsigned short,每个分量的取值范围为[0,65535]。上面的程序我们希望文字透明度为50%,所以设置alpha分量为0x8000,文字的颜色为0xffff(十进制的65535)。如果我们需要将取值范围[0,255]的分量,转换为XftColor分量可以使用如下公式:value*1.0/255.0*65535
编译以上代码,运行结果如下:

3.绘制点、线、矩形、弧形
绘制点
使用XDrawPoint向窗口中绘制一个点。
void DoPaint(Display *display, Window window){
GC gc = XCreateGC(display,window,0,nullptr);
XSetForeground(display,gc,0xff0000);
XDrawPoint(display,window,gc,15,15);
XFreeGC(display,gc);
}
以上程序在窗口的(15,15)坐标处绘制一个红色小点。由于一个像素显示不是很清晰,这里不再截图。
绘制线
Xlib中可以使用XDrawLine或是XDrawLines来绘制线段。XDrawLine示例程序如下:
void DoPaint(Display *display, Window window){
GC gc = XCreateGC(display,window,0,nullptr);
XSetForeground(display,gc,0xff0000);
XDrawLine(display,window,gc,15,15,30,30);
XFreeGC(display,gc);
}
以上程序将屏幕的坐标(15,15)作为起点,(30,30)作为终点绘制一条红色线段。
可以使用XSetLineAttributes来改变线条的样式,示例程序如下
void DoPaint(Display *display, Window window){
GC gc = XCreateGC(display,window,0,nullptr);
XSetForeground(display,gc,0xff0000);
XSetLineAttributes(display,gc,5,LineOnOffDash,CapButt, JoinMiter);
XDrawLine(display,window,gc,15,15,30,30);
XFreeGC(display,gc);
}
以上程序绘制了一条线条宽度为5像素,线条样式为虚线的线段。运行结果如下

使用XDrawLines可以一次性的画多条线段。这此我们仅简单展示linux操作系统下可以在窗口中做哪些事情,并不准备详细讲解每个函数和参数的意义。关于xlib相关开发资料极少,后续将搜集到的xlib api函数讲解以及相关的开发示例文档上传至互联网中,以便于后续查询需要。
绘制矩形
使用XDrawRectangle绘制矩形,示例程序如下
void DoPaint(Display *display, Window window){
GC gc = XCreateGC(display,window,0,nullptr);
XSetForeground(display,gc,0xff0000);
XSetLineAttributes(display,gc,5,LineSolid,CapButt, JoinMiter);
XDrawRectangle(display,window,gc,30,30,80,60);
XFreeGC(display,gc);
}
以上程序绘制了一个起点为30,30,宽度为80,高度为60,线条宽度为5像素,线条样式为实线的矩形。以上程序运行结果如下:

绘制弧形
使用XDrawArc绘制一个弧形,示例代码如下:
void DoPaint(Display *display, Window window){
GC gc = XCreateGC(display,window,0,nullptr);
XSetForeground(display,gc,0xff0000);
XSetLineAttributes(display,gc,5,LineSolid,CapButt, JoinMiter);
XDrawArc(display,window,gc,100,100,50,50,0*64,180*64);
XFreeGC(display,gc);
}
XDrawArc函数原型如下:
extern int XDrawArc(
Display* /* display */,
Drawable /* d */,
GC /* gc */,
int /* x */,
int /* y */,
unsigned int /* width */,
unsigned int /* height */,
int /* angle1 */,
int /* angle2 */
);
使用XDrawArc,设置的(x,y)并不是弧形的起点或是中心点坐标,而是弧形的左上角坐标,如下图所示,我使用XDrawArc绘制一个半圆,这个传入的(x,y)是下图所示矩形左上角坐标。angle1,和angle2是弧线起始的角度和偏移量,并且角度值需要乘以64。比如我想要在笛卡尔坐标系下第二象限的四分之一圆,我们从y轴的正方向画向x轴负方向,这样给angle1传入的参数就是90*64,从y轴正方向到x轴负方向经过了90度,所以angle2也是传入90*64。

编译运行以上程序,窗口显示如下

4.填充区域
我们可以使用XFillRectangle或XFillArc来填充一个矩形或是弧形区域。这里的XFillRectangle、XFillArc的参数与XDrawRectangle、XDrawArc相应的参数意义一致。唯一的不同是Draw是绘制图形,Fill是向图形中填充颜色。以下是示例程序
void DoPaint(Display *display, Window window){
GC gc = XCreateGC(display,window,0,nullptr);
XSetForeground(display,gc,0xff0000);
XFillArc(display,window,gc,100,100,50,50,0*64,180*64);
XSetForeground(display,gc,0x00ff00);
XFillRectangle(display,window,gc,200,200,100,80);
XFreeGC(display,gc);
}
编译以上程序,运行结果如下


7942

被折叠的 条评论
为什么被折叠?



