xeyes的重新实现--x系统及其Xlib以及和windows的异同

简介:
每一个X程序都对应一个x client,x server则只负责输入输出事件和请求,键盘和鼠标以及触摸屏之类的动作会被x server捕获,作为事件通过x协议传给x client,然后x client得到这些事件之后会根据它们做一些逻辑计算,然后得到一个请求通过x协议发给x server,此时x server就会根据x client的请求将图形绘制出来,通过输出设备显示。一个x server可以同时被很多个x client连接,因此它必须管理总体的图形布局,包括哪个窗口属于哪个x client,哪个窗口此时位于最上方,哪个窗口被遮住了哪个部分等等,这些同样作为事件--类似键盘鼠标等的输入事件传给相应的x client,因此可以想象,每一个可以显示的元素都要帮定一个或者几个x client,x client得到这些事件(管理总体布局的事件,而非输入设备的事件)之后,同样要做出一些反应,比如当它的窗口重新显示的时候要重画整个它的窗口等等。
     x系统是一个c/s结构的系统,它并不以窗口为核心,而是以进程(x client--xterm/xeyes,x server--xorg...)为核心的,x server进程负责捕获事件--包括输入设备的事件和窗口管理事件,并且将事件传递给相应的x client,而x client则根据事件负责逻辑处理,并将处理结果作为显示请求回传给x server,x server得到请求后进行显示。一般的,每一个x server都有一个事件队列和一个请求队列,每一个事件元素绑定x client,它的大体结构应该包含下面的逻辑:
1.
while(...) {
    1.从事件队列取出一个事件;
    2.得到事件队应的x client;
    3.将事件发给x client;
}
2.
while(...) {
    1.从请求队列取出一个请求;
    3.根据请求进行显示;
}
3.
3.0:根据鼠标的位置或者(以及)当前窗口得到一个或者多个对之感兴趣的x client用于下面3.1到3.x的事件构造;
3.1:鼠标回调:构造一个事件加入事件队列;
3.2:键盘回调:构造一个事件加入事件队列;
...
x client则同样拥有一个事件队列和一个请求队列,但是逻辑和x server正好相反。
作为一个例子,首先启动一个x server(将配置中的tcpip打开,否则别的机器没法连接),然后用xhost增加一个可以连接的主机,然后在该主机上设置DISPLAY环境变量,最后运行一个最简单的xeyes,然后到x server上移动一下鼠标,在x client上strace一下这个xeyes,希望得到的是当晃动鼠标的时候,strace会打印出tcpip连接的数据,可是结果却很乱,不停的select,不停的超时,不停的打印...看了xeyes的源码才知道,原来它使用的是timer,也就是说不管有没有事件,timer都会定期的到期,然后去select网络连接,也就是和x server的连接。这样岂不是很浪费资源,于是就想用最底层的xlib重写一下著名的xeyes,xlib可以说是比较底层的x编程库了,再往下就是直接用socket用x协议写代码了。顺便说一句,在xeyes的man手册中,有这么一句话:Xeyes watches what you do and reports to the Boss.然而它并没有向任何人报告你的一举一动,看了xeyes的代码后,突然觉得这是可以的,因为XQueryPointer函数可以返回足够的信息,它足以让你的Boss将你fire了,具体的方式就是在XQueryPointer之后以XQueryPointer的第四个参数child_return为参数调用XFetchName(我们简单一点说,实际上还有更多信息可以获取的),然后就可以得到鼠标当前在哪个窗口上,这样Boss就知道你在干什么了,这个信息很容易通过syslog发送出去的,不过前提是Boss的xeyes窗口必须隐藏,也就是必须隐藏那双眼睛。在下面的代码中,我将不处理报告Boss的逻辑,因为上面已经简单说过了。
     下面就是事件循环版本的xeyes的源代码了,它除了xlib没有使用其余的库,也没有使用轮询式的timer,而是使用了事件中断的触发机制,只有在有鼠标移动事件的时候xeyes才会行动,再者,所有和数学相关的代码完全拷贝timer版的xeyes代码,下面就是源代码:
#include <X11/Xlib.h>
#include <X11/Xos.h>
#include <stdio.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/extensions/shape.h>
#include <stdio.h> 
#include <math.h>
#include "transform.h"
int main(void) 
{
    Display *disp = XOpenDisplay(NULL);    //得到默认的display
    Window win_root = DefaultRootWindow(disp); //如果不使用这个root,将得不到xeyes窗口之外的事件。
    int screen = DefaultScreen(display);
    int black = BlackPixel(display, screen);
    int white = WhitePixel(display, screen);
    Window win_eyes = XCreateSimpleWindow(disp, win_root,  0, 0, 150, 100, 3, black, white);
    XSelectInput(disp, win_root, PointerMotionMask );
    XSelectInput(disp, win_eyes, ExposureMask|PointerMotionMask );
    XMapWindow(disp, win_eyes);
    XGCValues    xgcv1;
    xgcv1.function = GXorReverse;
    XtGCMask valuemask1 = GCFunction|GCForeground | GCBackground;
    GC gc_eyeballs = XCreateGC(disp, win_root, 0, NULL);
    XGCValues values_return;
    XGetGCValues (disp, gc_eyeballs, valuemask1, &values_return);
    Pixel fg = values_return.foreground;
    Pixel bg = values_return.background;
    xgcv1.foreground = bg;
    xgcv1.background = fg; //注意这里创建了两个反转的画笔,作用在于画眼珠子之前先擦除原来的眼珠子
    GC gc_balls = XCreateGC(disp, win_root, valuemask1, &xgcv1);
    XGCValues    xgcv;
    Pixmap pim = XCreatePixmap (disp, win_eyes, 150, 100, 1);
    XtGCMask valuemask = GCForeground | GCBackground;
    GC gc_eyes = XCreateGC (disp, pim, valuemask, &xgcv);
    XSetForeground (disp, gc_eyes, 0);
    XFillRectangle (disp, pim, gc_eyes, 0, 0,150, 100);
    XSetForeground (disp, gc_eyes, 1);
    Window        rep_root, rep_child;
    int        rep_rootx, rep_rooty;
    unsigned int    rep_mask;
    int        dx, dy;
    TPoint        mouse;
    TPoint        newpupil[2]; //记录当次的眼睛位置
    TPoint        oldpupil[2]; //记录上次的眼睛位置
    Transform    t; //涉及到数学的计算,全部拷贝原始的xeyes代码
    SetTransform (&t,0, 150,100, 0, W_MIN_X, W_MAX_X, W_MIN_Y, W_MAX_Y);
    while(1) {
        XEvent report = {0};
        XNextEvent(disp, &report);
        switch (report.type){
            case Expose:
                if (report.xexpose.window == win_root) {
                    //一般不会执行到这里,因为我们取的是根窗口,除非创建一个全屏幕的窗口使之遮住根窗口,然后移动它。
                } else { //以下的T打头的函数全部拷贝自原始代码
                    TFillArc (disp, pim, gc_eyes, &t,
                          EYE_X(0) - EYE_HWIDTH - EYE_THICK,
                           EYE_Y(0) - EYE_HHEIGHT - EYE_THICK,
                          EYE_WIDTH + EYE_THICK * 2.0,
                           EYE_HEIGHT + EYE_THICK * 2.0,
                           90 * 64, 360 * 64); //画左眼
                    TFillArc (disp, pim, gc_eyes, &t,
                          EYE_X(1) - EYE_HWIDTH - EYE_THICK,
                           EYE_Y(1) - EYE_HHEIGHT - EYE_THICK,
                          EYE_WIDTH + EYE_THICK * 2.0,
                           EYE_HEIGHT + EYE_THICK * 2.0,
                           90 * 64, 360 * 64); //画右眼
                           //裁剪窗口
                    XShapeCombineMask (disp, win_eyes, ShapeBounding, 0, 0, pim, ShapeSet);
                    TFillArc (disp, win_eyes, gc_eyeballs, &t,
                          EYE_X(0) - EYE_HWIDTH - EYE_THICK,
                           EYE_Y(0) - EYE_HHEIGHT - EYE_THICK,
                          EYE_WIDTH + EYE_THICK * 2.0,
                           EYE_HEIGHT + EYE_THICK * 2.0,
                           90*64, 360 * 64); //涂黑左眼
                            TFillArc (disp, win_eyes, gc_balls, &t,
                          EYE_X(0) - EYE_HWIDTH,
                           EYE_Y(0) - EYE_HHEIGHT,
                          EYE_WIDTH, EYE_HEIGHT,
                          90 * 64, 360 * 64); //描左眼圈
                    TFillArc (disp, win_eyes, gc_eyeballs, &t,
                          EYE_X(1) - EYE_HWIDTH - EYE_THICK,
                           EYE_Y(1) - EYE_HHEIGHT - EYE_THICK,
                          EYE_WIDTH + EYE_THICK * 2.0,
                           EYE_HEIGHT + EYE_THICK * 2.0,
                           90*64, 360 * 64); //涂黑右眼
                            TFillArc (disp, win_eyes, gc_balls, &t,
                          EYE_X(1) - EYE_HWIDTH,
                           EYE_Y(1) - EYE_HHEIGHT,
                          EYE_WIDTH, EYE_HEIGHT,
                           90*64, 360 * 64); //描右眼圈
                } //不要break,继续画眼珠子
            case MotionNotify:
                //得到当前鼠标的位置,还顺便得到了它所悬停的窗口信息
                XQueryPointer (disp, win_root, &rep_root, &rep_child, &rep_rootx, &rep_rooty, &dx, &dy, &rep_mask);
                mouse.x = Tx(dx, dy, &t);
                mouse.y = Ty(dx, dy, &t);
                computePupils (mouse, newpupil); //数学计算
                TFillArc (disp, win_eyes, gc_balls, &t,
                       oldpupil[0].x - BALL_WIDTH / 2.0,
                       oldpupil[0].y - BALL_HEIGHT / 2.0,
                       BALL_WIDTH, BALL_HEIGHT,
                      90 * 64, 360 * 64); //先用反色清除掉原来的左眼珠子
                TFillArc (disp, win_eyes, gc_eyeballs, &t,
                               newpupil[0].x - BALL_WIDTH / 2.0,
                               newpupil[0].y - BALL_HEIGHT / 2.0,
                               BALL_WIDTH, BALL_HEIGHT,
                               90 * 64, 360 * 64); //再用前景色填充左眼珠子
                oldpupil[0].x = newpupil[0].x;
                oldpupil[0].y = newpupil[0].y;
                TFillArc (disp, win_eyes, gc_balls, &t,
                               oldpupil[1].x - BALL_WIDTH / 2.0,
                               oldpupil[1].y - BALL_HEIGHT / 2.0,
                               BALL_WIDTH, BALL_HEIGHT,
                               90 * 64, 360 * 64); //右眼珠子同左眼珠子
                TFillArc (disp, win_eyes, gc_eyeballs, &t,
                               newpupil[1].x - BALL_WIDTH / 2.0,
                               newpupil[1].y - BALL_HEIGHT / 2.0,
                               BALL_WIDTH, BALL_HEIGHT,
                               90 * 64, 360 * 64);
                oldpupil[1].x = newpupil[1].x;
                oldpupil[1].y = newpupil[1].y;
                break;
        }
    }
    return 0;
}
Xlib编程很简单,只要几个要素齐备了,那么剩下的就是闭着眼睛敲代码了,首先是一个display--这是一个环境,然后是一个drawable--这是一张画布,它可以是一个windows,最后是一个GC--它是一支画笔,总的情况就是在display中用gc在drawable上作画,一个main函数框架就是:
main()
{
    1.得到一个display,作为disp;
    2.得到一个drawable,作为一个win;
    3.得到一支画笔,作为gc;
    4.注册自己感兴趣的事件告诉x server;
    5.编写一个事件循环:
    while (1) {
        5.1.得到一个x server发来的事件;
        5.2.处理该事件;
    }
}
linux版本的无timer的xeyes到此结束,那么windows的呢?

可以想象,windows的实现更加简单一些,当用vc创建一个win32工程之后,IDE就已经帮你完成一半工作了,仔细理解这一半的工作,发现它和上述xlib编程的main框架是如此的相似。相似之后还有些许不同,这些不同完全是windows的消息机制和x系统的架构机制不同所导致的,x系统的当然要更简单一些,x系统基于进程,而windows基于线程,因此windows的消息机制之所以复杂,那是因为它使用一些操作系统本身的机制,比如线程。如果我们将windows的xeyes应用有意简化,使之向xlib的代码靠拢,最后我们发现,它们更加一致。



 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1271163

相关文章
|
6月前
|
安全 数据安全/隐私保护 Windows
如何在Windows 10系统中查看已连接WiFi密码-亲测可用-优雅草卓伊凡
如何在Windows 10系统中查看已连接WiFi密码-亲测可用-优雅草卓伊凡
297 15
如何在Windows 10系统中查看已连接WiFi密码-亲测可用-优雅草卓伊凡
|
6月前
|
关系型数据库 虚拟化 UED
Omnissa Horizon Windows OS Optimization Tool 2503 - Windows 系统映像优化工具
Omnissa Horizon Windows OS Optimization Tool 2503 - Windows 系统映像优化工具
252 7
Omnissa Horizon Windows OS Optimization Tool 2503 - Windows 系统映像优化工具
|
1月前
|
运维 安全 Linux
【清爽加速】Windows 11 Pro 24H2-Emmy精简系统
“清爽加速”Windows 11 Pro 24H2 针对老旧或低配设备,通过精简系统、优化服务与简化装机流程,降低资源占用,提升运行流畅度,兼顾安全性与稳定性,让老设备也能轻松应对日常办公与轻度娱乐需求。
【清爽加速】Windows 11 Pro 24H2-Emmy精简系统
|
28天前
|
安全 数据安全/隐私保护 Windows
ZyperWin++使用教程!让Windows更丝滑!c盘飘红一键搞定!ZyperWin++解决系统优化、Office安装和系统激活
ZyperWin++是一款仅5MB的开源免费Windows优化工具,支持快速优化、自定义设置与垃圾清理,兼具系统加速、隐私保护、Office安装等功能,轻便无广告,小白也能轻松上手,是提升电脑性能的全能管家。
539 0
|
1月前
|
安全 搜索推荐 开发者
【适度精简】Windows 7 旗舰版-emmy精简系统
Windows 7旗舰版因硬件占用高、冗余组件多、兼容性差及缺乏安全更新等问题,逐渐难以满足用户需求。适度精简版通过去除无用组件、优化性能与安全性,提升老旧设备运行效率,增强兼容性与稳定性,同时保留用户熟悉的操作界面,降低学习成本,满足个性化需求,延续Windows 7的实用价值。
|
2月前
|
Ubuntu Linux Windows
windows11系统安装ubuntu系统详细步骤
安装后,您可以直接从商店启动应用程序来源
558 0
|
4月前
|
Ubuntu Linux 数据安全/隐私保护
Windows中安装WSL 2和Ubuntu系统的教程
回看这一路,有趣吧?你已经跨界成为了一个Windows和Linux的桥梁。期待在代码的世界里,把一切玩得风生水起!
238 13
|
3月前
|
持续交付 Windows
如何使用Sysprep准备Windows系统并使用自动应答
通过Sysprep准备Windows系统,可实现SID重置与系统定制。进入Sysprep后,可安装软件、设置默认桌面文件,并使用Windows SIM创建应答文件以实现自动化部署。适用于系统克隆与批量部署场景。
|
5月前
|
JSON 安全 数据可视化
Elasticsearch(es)在Windows系统上的安装与部署(含Kibana)
Kibana 是 Elastic Stack(原 ELK Stack)中的核心数据可视化工具,主要与 Elasticsearch 配合使用,提供强大的数据探索、分析和展示功能。elasticsearch安装在windows上一般是zip文件,解压到对应目录。文件,elasticsearch8.x以上版本是自动开启安全认证的。kibana安装在windows上一般是zip文件,解压到对应目录。elasticsearch的默认端口是9200,访问。默认用户是elastic,密码需要重置。
2527 0
|
7月前
|
算法 关系型数据库 测试技术
WHQL微软驱动签名方案,让驱动程序在Windows系统流畅运行
WHQL认证(Windows徽标认证)是微软设立的严格测试标准,旨在确保驱动程序的兼容性、稳定性和互通性。本文介绍了三种WHQL微软驱动签名方案:单系统签名、多系统签名和硬件兼容性测试方案,分别满足不同开发商的需求。通过WHQL认证,不仅能消除Windows安装警告,提升用户体验,还能获得“Designed for Windows”徽标授权,入列全球Windows Catalog及HCL产品表,提升品牌权威性和采购优先权。此外,访问微软OCA可获取错误反馈,助力产品质量改进。选择合适的签名方案,让驱动在Windows系统中流畅运行!

热门文章

最新文章