上一篇我们在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
编译通过之后执行这个代码,就可以看到如图所示的窗口了。注意,同样的,需要在桌面环境当中执行。