从零开始手敲次世代游戏引擎(八)

上一篇我们在Windows上面创建了一个最基本的窗体。本篇我们将在Linux上面创建一个窗体。

如果说Windows的窗体是深深植入系统的,那么Linux的窗体系统则更像一个外挂。在Linux当中,图形界面是交给一个叫做Xserver的服务进行绘制管理,而客户端程序实际上并不直接绘制画面,而是生成一系列画面绘制指令,通过一种事先定义好的协议(X协议),发送给Xserver。Xserver根据这些绘图指令进行实际的绘图。打个小小的比方,X协议就好比HTML,而Xserver就好比浏览器。

也就是说,Linux的图形界面部分,其实是client-server结构,具有天生的分布式能力。事实上,你可以在你的安卓手机上安装一个Xserver,然后让远程的服务器将绘图指令发送到你的手机上,这样桌面就是由你的手机的硬件绘制的。

这和VNC技术以及RDP技术(Windows远程桌面技术)有着本质的不同。VNC/RDP技术的图形绘制是发生在远程机器的,而本地机器只是播放远程机器传来的实时视频流。

XCB则是X协议的C绑定。通过XCB,我们可以方便地生成遵循X协议指令,最终将这些指令发给Xserver完成绘制。

接下来就让我们通过XCB创建一个Linux的窗口。在我们的项目里新建一个Platform/Linux目录,在其中创建一个helloengine_xcb.c,然后输入如下代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <xcb/xcb.h>

int main(void) {
        xcb_connection_t        *pConn;
        xcb_screen_t            *pScreen;
        xcb_window_t            window;
        xcb_gcontext_t          foreground;
        xcb_gcontext_t          background;
        xcb_generic_event_t     *pEvent;
        uint32_t                mask = 0;
        uint32_t                values[2];
        uint8_t                 isQuit = 0;

        char title[] = "Hello, Engine!";
        char title_icon[] = "Hello, Engine! (iconified)";

        /* establish connection to X server */
        pConn = xcb_connect(0, 0);

        /* get the first screen */
        pScreen = xcb_setup_roots_iterator(xcb_get_setup(pConn)).data;

        /* get the root window */
        window = pScreen->root;

        /* create black (foreground) graphic context */
        foreground = xcb_generate_id(pConn);
        mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
        values[0] = pScreen->black_pixel;
        values[1] = 0;
        xcb_create_gc(pConn, foreground, window, mask, values);

        /* create white (background) graphic context */
        background = xcb_generate_id(pConn);
        mask = XCB_GC_BACKGROUND | XCB_GC_GRAPHICS_EXPOSURES;
        values[0] = pScreen->white_pixel;
        values[1] = 0;
        xcb_create_gc(pConn, background, window, mask, values);

        /* create window */
        window = xcb_generate_id(pConn);
        mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
        values[0] = pScreen->white_pixel;
        values[1] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS;
        xcb_create_window (pConn,                                       /* connection */
                                           XCB_COPY_FROM_PARENT,        /* depth */
                                           window,                                      /* window ID */
                                           pScreen->root,                       /* parent window */
                                           20, 20,                                      /* x, y */
                                           640, 480,                            /* width, height */
                                           10,                                          /* boarder width */
                                           XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class */
                                           pScreen->root_visual,        /* visual */
                                           mask, values);                       /* masks */

        /* set the title of the window */
        xcb_change_property(pConn, XCB_PROP_MODE_REPLACE, window,
                            XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8,
                            strlen(title), title);

        /* set the title of the window icon */
        xcb_change_property(pConn, XCB_PROP_MODE_REPLACE, window,
                            XCB_ATOM_WM_ICON_NAME, XCB_ATOM_STRING, 8,
                            strlen(title_icon), title_icon);

        /* map the window on the screen */
        xcb_map_window(pConn, window);

        xcb_flush(pConn);

        while((pEvent = xcb_wait_for_event(pConn)) && !isQuit) {
                switch(pEvent->response_type & ~0x80) {
                case XCB_EXPOSE:
                        break;
                case XCB_KEY_PRESS:
                        isQuit = 1;
                        break;
                }
                free(pEvent);
        }

        return 0;
}

用clang编译这个代码。注意需要链接xcb库。如果clang抱怨找不到这个库,请用yum或者apt安装。在CentOS里这个包叫xcb-devel,在Ubuntu里应该是叫libxcb-dev(未亲自验证)

tim@iZuf6iup3mphicesefdwajZ:~/src/GameEngineFromScratch/Platform/Linux$ clang -lxcb -o helloengine_xcb helloengine_xcb.c

编译通过之后执行这个代码,就可以看到如图所示的窗口了。注意,同样的,需要在桌面环境当中执行。

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

您正在使用您的 WordPress.com 账号评论。 注销 /  更改 )

Facebook photo

您正在使用您的 Facebook 账号评论。 注销 /  更改 )

Connecting to %s

%d 博主赞过: