原文链接:Programming OpenGL in Linux: GLX and Xlib - OpenGL Wiki

Jump to navigationJump to search

Xlib is a library which provides functions for applications running under the X Window System (also referred to as X). This includes window management as well as event handling. X is a network-oriented system: An application which is running on computer A can send its graphical output to computer B, which is located somewhere else in the network (the network can be a LAN as well as the internet), and can receive events like keyboard or mouse input from computer B. This requires that a program called the "X-Server" is running on both computers. In Linux, the X-server is started with the command startx. You will most probably not have to start the X-server manually, because most Linux Distributions will set up the system to automatically start X after booting.

In the following, a small program framework will be developed, which uses the GLX extension to the X windows system. Only little knowledge of X will be needed; for a comprehensive introduction to X the "Xlib Programming Manual" by Adrian Nye can be recommended.

The program starts with the inclusion of the header files:

#include<stdio.h>
#include<stdlib.h>
#include<X11/X.h>
#include<X11/Xlib.h>
#include<GL/gl.h>
#include<GL/glx.h>
#include<GL/glu.h>

stdio.h and stdlib.h are included because the functions printf() and exit() will be used. X11/X.h and GL/gl.h are not necessarily needed in the code, because they will be included by X11/Xlib.h and GL/glx.h automatically. They are mentioned for completeness.

We will need the following variables:

Display                 *dpy;
Window                  root;
GLint                   att[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None };
XVisualInfo             *vi;
Colormap                cmap;
XSetWindowAttributes    swa;
Window                  win;
GLXContext              glc;
XWindowAttributes       gwa;
XEvent                  xev;

Their purpose will be explained below. Since the program should somehow be related to OpenGL, we create a function which uses OpenGL to display something. What about...a quad? With different colors, maybe at each vertex?

void DrawAQuad() {glClearColor(1.0, 1.0, 1.0, 1.0);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glMatrixMode(GL_PROJECTION);glLoadIdentity();glOrtho(-1., 1., -1., 1., 1., 20.);glMatrixMode(GL_MODELVIEW);glLoadIdentity();gluLookAt(0., 0., 10., 0., 0., 0., 0., 1., 0.);glBegin(GL_QUADS);glColor3f(1., 0., 0.); glVertex3f(-.75, -.75, 0.);glColor3f(0., 1., 0.); glVertex3f( .75, -.75, 0.);glColor3f(0., 0., 1.); glVertex3f( .75,  .75, 0.);glColor3f(1., 1., 0.); glVertex3f(-.75,  .75, 0.);glEnd();
} 

Afterwards, we define the main program and the first call to Xlib.

int main(int argc, char *argv[]) {dpy = XOpenDisplay(NULL);if(dpy == NULL) {printf("\n\tcannot connect to X server\n\n");exit(0);}

The argument to XOpenDisplay() is NULL. This means the graphical output will be sent to the computer on which it is executed.

Next, a handle to the root window is needed.

root = DefaultRootWindow(dpy);

The root window is the "desktop background" window.

Before we continue, we have to think about what OpenGL capabilities the program needs: The color depth, depth buffer and/or double buffer, stencil buffer etc. Remember the variable att[] (visual attributes) we defined at the beginning: The value GLX_RGBA tells GLX that the color depth shall be true color. The depth buffer shall be 24 bits deep, and a double buffer shall be used. The list is terminated by the value None. More possible options can be found in GL/glx.h. If you want to know about the capabilities that your graphics adapter provides, open a shell and type glxinfo. You will get information about your OpenGL driver and which extensions it supports. At the end of the output all provided visuals are listed, with different properties concerning color depth, depth/double/stencil buffer etc. To select a visual which matches our needs, we call

vi = glXChooseVisual(dpy, 0, att);if(vi == NULL) {printf("\n\tno appropriate visual found\n\n");exit(0);
}
else {printf("\n\tvisual %p selected\n", (void *)vi->visualid); /* %p creates hexadecimal output like in glxinfo */
}

If glXChooseVisual returns with success, the visual's id will be output. If NULL is returned, there is no visual that fulfills your needs. In that case, check the output of glxinfo again. Maybe you have to use a different depth buffer size (GLX_DEPTH_SIZE, 16 instead of GLX_DEPTH_SIZE, 24), or you could even have to remove the GLX_DEPTH_SIZE (or the GLX_DOUBLEBUFFER) entry. This should be considered especially if you want to create programs not only for your computer, but for other ones: You should code your program in a way that it can check a list of different combinations of visual attributes, because the capabilities depend heavily on the hardware.

Now we create a Colormap for the window:

cmap = XCreateColormap(dpy, root, vi->visual, AllocNone);

Then, a structure of the type XSetWindowAttributes has to be initialized. The complete structure definition can be found in X11/Xlib.h, we need only 2 fields to be filled with values:

 swa.colormap = cmap;swa.event_mask = ExposureMask | KeyPressMask;

This tells the X server that the colormap we created before shall be used for the window, and the window shall respond to Exposure and KeyPress events (this will be explained later). At this point, the window can be created:

win = XCreateWindow(dpy, root, 0, 0, 600, 600, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &swa);

The arguments are : The display pointer (dpy), which determines on which display the window shall be created. It is indeed possible to call XOpenDisplay twice (or more often) within a program, each time getting a new Display pointer. This allows the program to send graphical output not only to the local computer, but as well to a remote one.

The handle of the root window is passed as the parent window. Each window- except the root window- has a parent; a window whose parent is the root window is often called top-level window. top-level windows have decorations (title bar, minimize-/maximize-button etc.), which are provided by the window-manager (in linux there are quite a lot of different window managers. some make your windows look like you are running win95 or mac os).

The initial x-/y-position for the window is (0/0). The initial window position always refers to the parent window (which is the root window here). It should be mentioned that these values are ignored by most windows managers, which means that top-level windows maybe placed somewhere else on the desktop. But if you create a child window within a top-level window, these values are used.

The next values are window width and height (600x600 pixels).

The border width is set to 0, like the initial window position, it is meaningless for top-level windows, too.

Then comes the depth, which is defined in the XVisualInfo structure *vi.

The window type is InputOutput. There are other types, but you will probably never need them.

The next parameter is a bitwise-OR of the values CWColormap and CWEventMask. This tells the X server which fields of the XSetWindowAttributes structure swa were filled by the program and should be taken into account when creating the window. Finally, a pointer to the structure itself is passed.

XCreateWindow returns a window id (which is actually just a long int). Now we make the window appear

 XMapWindow(dpy, win);

and change the string in the title bar:

XStoreName(dpy, win, "VERY SIMPLE APPLICATION");

One thing is still missing: Since we want to display 3D things with OpenGL, we have to create a GL context and bind it to the window

 glc = glXCreateContext(dpy, vi, NULL, GL_TRUE);glXMakeCurrent(dpy, win, glc);

The last parameter decides if direct rendering is enabled. If you want to send the graphical output via network, you have to set it to GL_FALSE. If your application puts its output to the computer you are sitting in front of, use GL_TRUE. Note that some capabilities like vertex buffer objects can only be used with a direct gl context (GL_TRUE).

After creating the context, it is made current to our top-level window.

Since we wanted to use depth-buffering (using GLX_DEPTH_SIZE), we should enable the depth test:

glEnable(GL_DEPTH_TEST); 

Remember how we decided which events our application should listen to by setting the swa.event_mask field? Now we have to make the application listen:

 while(1) {XNextEvent(dpy, &xev);if(xev.type == Expose) {XGetWindowAttributes(dpy, win, &gwa);glViewport(0, 0, gwa.width, gwa.height);DrawAQuad(); glXSwapBuffers(dpy, win);}else if(xev.type == KeyPress) {glXMakeCurrent(dpy, None, NULL);glXDestroyContext(dpy, glc);XDestroyWindow(dpy, win);XCloseDisplay(dpy);exit(0);}} /* this closes while(1) { */
} /* this is the } which closes int main(int argc, char *argv[]) { */

With while(1) an infinite loop is started. In this loop, XNextEvent is called. This function blocks program execution until one of the events that we allowed (with ExposureMask and KeyPressMask in the XSetWindowAttributes structure) occurs. Note that the program consumes very little cpu time while waiting for the next event.

If an event is received, the XEvent structure xev is filled with information. Since we permitted two kinds of events, we have to check what event it is (with xev.type == ...). In case it is an Exposure Event, we get information about the current window size (XGetWindowAttributes) and resize the viewport, then DrawAQuad and finally swap the buffers (remember that we did choose a double-buffered visual).

Still you wonder what an Exposure Event is: On the desktop, you usually have many windows overlapping each other. If a part of a window- which was occluded by another window before- appears back on the screen, because the window by which it was occluded is minimized, moved or closed, such an event is generated. Another reason for an Exposure Event could be that a window is being resized. In short terms: Exposure Events are generated when the system thinks that the content of a window should be updated.

If a key is pressed (xev.type == KeyPress), the program is terminated. To make it in a clean way, the GL context binding to the window is released (glXMakeCurrent(dpy, None, NULL);), and the GL context is destroyed. Then we kill the window, close the display and exit the program.

You may have noted one thing: We selected swa.event_mask = ExposureMask | KeyPressMask. In the event loop we checked for xev.type == KeyPress, but for xev.type == Expose instead of xev.type == Exposure. The event masks do not always correspond to the event names. You can check this in X11/X.h. Or buy the Xlib Programming Manual...

Now we put all the stuff together:

// -- Written in C -- //#include<stdio.h>
#include<stdlib.h>
#include<X11/X.h>
#include<X11/Xlib.h>
#include<GL/gl.h>
#include<GL/glx.h>
#include<GL/glu.h>Display                 *dpy;
Window                  root;
GLint                   att[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None };
XVisualInfo             *vi;
Colormap                cmap;
XSetWindowAttributes    swa;
Window                  win;
GLXContext              glc;
XWindowAttributes       gwa;
XEvent                  xev;void DrawAQuad() {glClearColor(1.0, 1.0, 1.0, 1.0);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glMatrixMode(GL_PROJECTION);glLoadIdentity();glOrtho(-1., 1., -1., 1., 1., 20.);glMatrixMode(GL_MODELVIEW);glLoadIdentity();gluLookAt(0., 0., 10., 0., 0., 0., 0., 1., 0.);glBegin(GL_QUADS);glColor3f(1., 0., 0.); glVertex3f(-.75, -.75, 0.);glColor3f(0., 1., 0.); glVertex3f( .75, -.75, 0.);glColor3f(0., 0., 1.); glVertex3f( .75,  .75, 0.);glColor3f(1., 1., 0.); glVertex3f(-.75,  .75, 0.);glEnd();
} int main(int argc, char *argv[]) {dpy = XOpenDisplay(NULL);if(dpy == NULL) {printf("\n\tcannot connect to X server\n\n");exit(0);}root = DefaultRootWindow(dpy);vi = glXChooseVisual(dpy, 0, att);if(vi == NULL) {printf("\n\tno appropriate visual found\n\n");exit(0);} else {printf("\n\tvisual %p selected\n", (void *)vi->visualid); /* %p creates hexadecimal output like in glxinfo */}cmap = XCreateColormap(dpy, root, vi->visual, AllocNone);swa.colormap = cmap;swa.event_mask = ExposureMask | KeyPressMask;win = XCreateWindow(dpy, root, 0, 0, 600, 600, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &swa);XMapWindow(dpy, win);XStoreName(dpy, win, "VERY SIMPLE APPLICATION");glc = glXCreateContext(dpy, vi, NULL, GL_TRUE);glXMakeCurrent(dpy, win, glc);glEnable(GL_DEPTH_TEST); while(1) {XNextEvent(dpy, &xev);if(xev.type == Expose) {XGetWindowAttributes(dpy, win, &gwa);glViewport(0, 0, gwa.width, gwa.height);DrawAQuad(); glXSwapBuffers(dpy, win);}else if(xev.type == KeyPress) {glXMakeCurrent(dpy, None, NULL);glXDestroyContext(dpy, glc);XDestroyWindow(dpy, win);XCloseDisplay(dpy);exit(0);}} /* this closes while(1) { */
} /* this is the } which closes int main(int argc, char *argv[]) { */

This program is written in plain C (although i, personally, prefer C++ syntax). Save it as quad.c, and compile it with

gcc -o quad quad.c -lX11 -lGL -lGLU

The -l flags refer to the Xlib, the OpenGL lib and the GLU lib. An executable named quad is created.

Programming OpenGL in Linux: GLX and Xlib相关推荐

  1. Linux中opengl库叫什么名字,Linux下的OpenGL——Mesa和GLX简介

    一.什么是Mesa和GLX 众所周知,OpenGL作为图形界的工业标准,其仅仅定义了一组2D和3D图形接口API,而对于窗口管理.IO消息响应等并没有规定.也就是说,OpenGL依赖各平台提供用于渲染 ...

  2. Linux下的OpenGL——Mesa和GLX简介

    一.什么是Mesa和GLX 众所周知,OpenGL作为图形界的工业标准,其仅仅定义了一组2D和3D图形接口API,而对于窗口管理.IO消息响应等并没有规定.也就是说,OpenGL依赖各平台提供用于渲染 ...

  3. 贴图问题,opengl,linux,windows,消除锯齿,摩尔纹,yuv 还是 rgb

    1 消除锯齿和摩尔纹 windows下使用d3d是很方便的,基本不用设置很多东西,就可以做到,所以windows上最好使用d3d.但是linux上有所不同. 摩尔条纹是两条线或两个物体之间以恒定的角度 ...

  4. linux qt “QXcbIntegration: Cannot create platform OpenGL context, neither GLX nor EGL are enable“

    需要将[/home/jakauser/Qt5.9.9/5.9.9/gcc_64/plugins/xcbglintegrations]文件夹也复制过去

  5. linux系统中XLib在什么位置,什么是glibc?glibc是什么?什么是freetype?freetype是什么?什么是?Xlib是什么?什么是lo...

    什么是glibc?glibc是什么?什么是freetype?freetype是什么?什么是?Xlib是什么?什么是lo 本站整理   发布时间:2009-03-10 15:26:55   作者:jb5 ...

  6. opengl arm linux,开源头条 | ARM v9架构发布

    ARM v9架构发布 Arm昨日发布ARMv9架构,专注于高性能计算.机器学习.数字信号处理和安全 性. ARMv9引入了Arm的Confidential Compute Architecture(C ...

  7. beginning linux programming pdf,Beginning Linux Programming, Second Edition

    摘要: If you've already got Linux up and running on your machine and you really want to exploit its ca ...

  8. 配置OpenGL(Linux)

    这里介绍了在Linux系统使用OpenGL可能会用到的库的安装方法,这些库包括:freeglut, glew, GLTools, glfw; 安装基本的编译环境 sudo apt-get instal ...

  9. linux 查看opengl版本,linux下查看OpenGL版本

    http://hi.baidu.com/yeyaxx/blog/item/852df9118b383e67cb80c440.html http://hi.baidu.com/youzuo111/blo ...

最新文章

  1. [UVA 10827] Maximum sum on a torus
  2. 如何查看Oracle的用户权限
  3. 老男孩Linux运维第41期20171016第六周学习重点课堂记录
  4. WPF 用Popup做下拉菜单
  5. C语言 va_start 宏 - C语言零基础入门教程
  6. mojoportal升级中用户相关设置
  7. 分类算法python程序_分类算法——k最近邻算法(Python实现)(文末附工程源代码)...
  8. 任务计划程序设置软件在电脑重启后启动
  9. c++中char * 和 char []的区别-转
  10. matlab中 晶闸管整流桥导通角_逆变角如何设置,晶闸管2011-6-6
  11. 批量管理微信社群,社群助手
  12. cocos 层级渲染与管理
  13. D. Relatively Prime Graph codeforces 1009 D
  14. 天翼云服务器ubuntu20.04安装Mysql记录
  15. python3+selenium3+ie9初体验
  16. 什么是软件设计中的上游和下游?
  17. 期货开户手续费的秘密成了透明
  18. Selenium UnreachableBrowserException异常处理方案
  19. linux的基本指令--第三节
  20. logback实现日志脱敏

热门文章

  1. 【图神经网络】SGC:简化图卷积网络
  2. 运放的几个应用:精密整流电路、理想二极管
  3. Arrays和数组的转换
  4. krpano教程:Videoplayer插件中文说明
  5. Linux(Centos7)无法读取ntfs的U盘及ntfs-3g安装
  6. uniapp热更新(wgt)
  7. window下MySQL的压缩包方式安装--单版本或多版本(5.7和8共存)通用
  8. OpenShift 4 - 从 1.3G 到 50M,以最小化的可执行程序运行 Quarkus 微服务
  9. 10 款更先进的开源命令行工具
  10. CSDN Markdown模板