(译)SimpleGLXWindow类

介绍

在OpenGL游戏编程 - 第二版发布之后,很明显,最后一章中使用的SimpleGLXWindow类没有记载在书中!所以,对于那些喜欢快速开始的Linux用户来说,下面就是了!

类结构

SimpleGLXWindow的类结构与Win32版本非常相似。像Win32版本一样,它继承了BOGLGPWindow基类,因此实现了相同的虚拟方法。这里是类定义的参考:

class SimpleGLXWindow : public BOGLGPWindow
{
public:
    SimpleGLXWindow(); //default constructor
    virtual ~SimpleGLXWindow();

    bool create(int width, int height, int bpp, bool fullscreen);
    void destroy();
    void processEvents();
    void attachExample(Example* example);

    bool isRunning(); //Is the window running?

    void swapBuffers() { glXSwapBuffers(m_display, m_XWindow); }

    float getElapsedSeconds();

    KeyboardInterface* getKeyboard() const { return m_keyboard; }
    MouseInterface* getMouse() const { return m_mouse; }
private:
    Example* m_example; //A link to the example program
    bool m_isRunning; //Is the window still running?


    Example* getAttachedExample() { return m_example; }

    unsigned int m_lastTime;

    Display* m_display;
    Window m_XWindow;
    GLXContext m_glContext;
    XF86VidModeModeInfo m_XF86DeskMode;
    XSetWindowAttributes m_XSetAttr;
    int m_screenID;

    bool m_isFullscreen;
    unsigned int m_width;
    unsigned int m_height;
    unsigned int m_bpp;

    bool m_GL3Supported;

    KeyboardInterface* m_keyboard;
    MouseInterface* m_mouse;
};

窗口创建

窗口类最重要最复杂的部分是创建OpenGL窗口的代码。我们要做的第一件事就是使用XOpenDisplay获取默认显示的句柄,通过传递一个零(或NULL),这将获取DISPLAY环境变量中设置的任何显示(通常是用来查看桌面的那个) !)。

m_display = XOpenDisplay(0);  //Open default display
if (m_display == NULL)
{
    std::cerr << "Could not open the display" << std::endl;
    return false;
}

所以,我们已经抓住了默认显示,如果有错误,我们已经记录了它,并返回false以指示窗口创建失败。接下来,我们得到一个标识显示器默认屏幕的句柄:

m_screenID = DefaultScreen(m_display); //Get the default screen id
 ```
现在,我们列出可用的显示模式,看看是否符合我们的要求。如果不是的话,我们会退出一个错误:

XF86VidModeModeInfo **modes;
if (!XF86VidModeGetAllModeLines(m_display, m_screenID, &modeNum, &modes))
{
std::cerr << “Could not query the video modes” << std::endl;
return false;
}

int bestMode = -1;
for (int i = 0; i < modeNum; i++)
{
if ((modes[i]->hdisplay == width) &&
(modes[i]->vdisplay == height))
{
bestMode = i;
}
}

if (bestMode == -1)
{
std::cerr << “Could not find a suitable graphics mode” << std::endl;
return false;
}
“`
在我们存储了最佳匹配的显示模式之后,我们要求一个具有16位深度缓冲区的双缓冲窗口:

int doubleBufferedAttribList [] = {
    GLX_RGBA, GLX_DOUBLEBUFFER,
    GLX_RED_SIZE, 4,
    GLX_GREEN_SIZE, 4,
    GLX_BLUE_SIZE, 4,
    GLX_DEPTH_SIZE, 16,
    None
};

XVisualInfo* vi = NULL;
//Attempt to create a double buffered window
vi = glXChooseVisual(m_display, m_screenID, doubleBufferedAttribList);

if (vi == NULL)
{
    std::cerr << "Could not create a double buffered window" << std::endl;
    return false;
}
 ```
下一步是创建一个OpenGL 2.1上下文,所以我们可以请求一个GL3。这些是我们必须采取的与Windows相同的步骤:

//Create a GL 2.1 context
GLXContext gl2Context = glXCreateContext(m_display, vi, 0, GL_TRUE);

if (gl2Context == NULL)
{
std::cerr << “Could not create a GL 2.1 context, please check your graphics drivers” << std::endl;
return false;
}

//Get a pointer to the GL 3.0 context creation
PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((GLubyte*)”glXCreateContextAttribsARB”);

if (glXCreateContextAttribs == NULL)
{
std::cerr << “OpenGL 3.0 is not supported, falling back to 2.1” << std::endl;
m_glContext = gl2Context;
m_GL3Supported = false;
}
else
{
//Create a GL 3.0 context

int attribs[] = {
    GLX_CONTEXT_MAJOR_VERSION_ARB, 3,//we want a 3.0 context
    GLX_CONTEXT_MINOR_VERSION_ARB, 0,
    0 //zero indicates the end of the array
};

m_glContext = glXCreateContextAttribs(m_display, framebufferConfig, 0, true, &attribs[0]);
glXDestroyContext(m_display, gl2Context); //We can destroy the GL 2.0 context once the 3.0 one has bene created
m_GL3Supported = true;

}
“`
如果不支持OpenGL 3.0,我们设置一个标志,以便我们可以使用后备2.1着色器。

现在我们有足够的信息来实际创建窗口。记得我们存储了我们能找到的最好的显示模式吗?在设置了一些窗口配置设置后,我们使用下面的代码:

Colormap cmap = XCreateColormap(m_display, RootWindow(m_display, vi->screen),vi->visual, AllocNone);
m_XSetAttr.colormap = cmap;
m_XSetAttr.border_pixel = 0;
m_XSetAttr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask |
                            StructureNotifyMask;

m_XSetAttr.override_redirect = False;

unsigned long windowAttributes = CWBorderPixel | CWColormap | CWEventMask;

if (fullscreen)
{
    windowAttributes = CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect;

    XF86VidModeSwitchToMode(m_display, m_screenID, modes[bestMode]);
    XF86VidModeSetViewPort(m_display, m_screenID, 0, 0);
    m_XSetAttr.override_redirect = True;
}

m_XWindow = XCreateWindow(m_display, RootWindow(m_display, vi->screen),
                              0, 0, width, height, 0, vi->depth, InputOutput, vi->visual,
                              CWBorderPixel | CWColormap | CWEventMask, &m_XSetAttr);
 ```

最后,我们设置窗口标题,如果我们是全屏,我们抓住光标:

if (fullscreen)
{
XWarpPointer(m_display, None, m_XWindow, 0, 0, 0, 0, 0, 0);
XMapRaised(m_display, m_XWindow);
XGrabKeyboard(m_display, m_XWindow, True, GrabModeAsync, GrabModeAsync, CurrentTime);
XGrabPointer(m_display, m_XWindow, True, ButtonPressMask,
GrabModeAsync, GrabModeAsync, m_XWindow, None, CurrentTime);

m_isFullscreen = true;

}
else
{
Atom wmDelete = XInternAtom(m_display, “WM_DELETE_WINDOW”, True);
XSetWMProtocols(m_display, m_XWindow, &wmDelete, 1);
XSetStandardProperties(m_display, m_XWindow, title.c_str(), None, NULL, NULL, 0, NULL);
XMapRaised(m_display, m_XWindow);
}

XFree(modes);


上面的最后一行释放了我们之前搜索的显示模式的内存。

破坏窗口

这很简单,代码如下:

void SimpleGLXWindow::destroy()
{
m_mouse->showCursor(true);
if (m_glContext)
{
glXMakeCurrent(m_display, None, NULL);
glXDestroyContext(m_display, m_glContext);
m_glContext = NULL;
}

if (m_isFullscreen)
{
    XF86VidModeSwitchToMode(m_display, m_screenID, &m_XF86DeskMode);
    XF86VidModeSetViewPort(m_display, m_screenID, 0, 0);
}

XCloseDisplay(m_display);

}
“`
基本上,我们确保光标是可见的,然后我们销毁OpenGL上下文,最后在完成时切换显示模式。我们在显示器上释放我们的手柄,窗户被完全破坏。

如果您还有其他问题,无论是关于本文还是本书,或者在论坛上告诉我,或者在Twitters上戳我。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值