2010-07-10 08:20:50 +00:00
|
|
|
/* ZZ Open GL graphics plugin
|
|
|
|
* Copyright (c)2009-2010 zeydlitz@gmail.com, arcum42@gmail.com
|
|
|
|
* Based on Zerofrog's ZeroGS KOSMOS (c)2005-2008
|
2010-03-19 02:04:55 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
2010-07-04 22:49:00 +00:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
2010-03-19 02:04:55 +00:00
|
|
|
*/
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2010-09-01 10:28:37 +00:00
|
|
|
#include "Util.h"
|
|
|
|
#include "GLWin.h"
|
2010-03-19 02:04:55 +00:00
|
|
|
|
|
|
|
#ifdef GL_X11_WINDOW
|
|
|
|
|
2010-06-11 11:48:07 +00:00
|
|
|
#include <X11/Xlib.h>
|
2010-09-08 11:16:27 +00:00
|
|
|
#include <stdlib.h>
|
2010-06-11 11:48:07 +00:00
|
|
|
|
2010-03-19 02:04:55 +00:00
|
|
|
bool GLWindow::CreateWindow(void *pDisplay)
|
2010-04-25 00:31:27 +00:00
|
|
|
{
|
2010-09-05 16:30:47 +00:00
|
|
|
// init support of multi thread
|
|
|
|
if (!XInitThreads())
|
|
|
|
ZZLog::Error_Log("Failed to init the xlib concurent threads");
|
|
|
|
|
2010-05-01 20:33:53 +00:00
|
|
|
glDisplay = XOpenDisplay(0);
|
|
|
|
glScreen = DefaultScreen(glDisplay);
|
2010-03-19 02:04:55 +00:00
|
|
|
|
2010-05-01 20:33:53 +00:00
|
|
|
if (pDisplay == NULL) return false;
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2010-05-01 20:33:53 +00:00
|
|
|
*(Display**)pDisplay = glDisplay;
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2010-05-01 20:33:53 +00:00
|
|
|
return true;
|
2010-03-19 02:04:55 +00:00
|
|
|
}
|
|
|
|
|
2010-09-01 10:28:37 +00:00
|
|
|
bool GLWindow::ReleaseContext()
|
2010-04-25 00:31:27 +00:00
|
|
|
{
|
2010-09-06 22:21:07 +00:00
|
|
|
bool status = true;
|
|
|
|
if (!glDisplay) return status;
|
|
|
|
|
|
|
|
// free the context
|
|
|
|
if (context)
|
2010-03-19 06:38:44 +00:00
|
|
|
{
|
2010-09-06 22:21:07 +00:00
|
|
|
if (!glXMakeCurrent(glDisplay, None, NULL)) {
|
2010-04-25 08:33:05 +00:00
|
|
|
ZZLog::Error_Log("Could not release drawing context.");
|
2010-09-06 22:21:07 +00:00
|
|
|
status = false;
|
|
|
|
}
|
2010-09-01 10:28:37 +00:00
|
|
|
|
2010-03-19 06:38:44 +00:00
|
|
|
glXDestroyContext(glDisplay, context);
|
|
|
|
context = NULL;
|
|
|
|
}
|
2010-09-01 10:28:37 +00:00
|
|
|
|
2010-09-06 22:21:07 +00:00
|
|
|
// free the visual
|
|
|
|
if (vi) {
|
|
|
|
XFree(vi);
|
|
|
|
vi = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
2010-09-01 10:28:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GLWindow::CloseWindow()
|
|
|
|
{
|
|
|
|
SaveConfig();
|
2010-09-05 15:47:09 +00:00
|
|
|
if (!glDisplay) return;
|
2010-03-19 06:38:44 +00:00
|
|
|
|
2010-09-05 15:47:09 +00:00
|
|
|
XCloseDisplay(glDisplay);
|
|
|
|
glDisplay = NULL;
|
2010-03-19 02:04:55 +00:00
|
|
|
}
|
|
|
|
|
2010-09-01 10:28:37 +00:00
|
|
|
bool GLWindow::CreateVisual()
|
2010-03-19 02:04:55 +00:00
|
|
|
{
|
|
|
|
// attributes for a single buffered visual in RGBA format with at least
|
|
|
|
// 8 bits per color and a 24 bit depth buffer
|
2010-04-25 00:31:27 +00:00
|
|
|
int attrListSgl[] = {GLX_RGBA, GLX_RED_SIZE, 8,
|
|
|
|
GLX_GREEN_SIZE, 8,
|
|
|
|
GLX_BLUE_SIZE, 8,
|
2010-03-19 02:04:55 +00:00
|
|
|
GLX_DEPTH_SIZE, 24,
|
2010-05-01 20:33:53 +00:00
|
|
|
None
|
|
|
|
};
|
2010-03-19 02:04:55 +00:00
|
|
|
|
2010-04-25 00:31:27 +00:00
|
|
|
// attributes for a double buffered visual in RGBA format with at least
|
2010-03-19 02:04:55 +00:00
|
|
|
// 8 bits per color and a 24 bit depth buffer
|
2010-04-25 00:31:27 +00:00
|
|
|
int attrListDbl[] = { GLX_RGBA, GLX_DOUBLEBUFFER,
|
|
|
|
GLX_RED_SIZE, 8,
|
|
|
|
GLX_GREEN_SIZE, 8,
|
|
|
|
GLX_BLUE_SIZE, 8,
|
2010-03-19 02:04:55 +00:00
|
|
|
GLX_DEPTH_SIZE, 24,
|
2010-05-01 20:33:53 +00:00
|
|
|
None
|
|
|
|
};
|
2010-03-19 02:04:55 +00:00
|
|
|
|
|
|
|
/* get an appropriate visual */
|
2010-03-19 06:38:44 +00:00
|
|
|
vi = glXChooseVisual(glDisplay, glScreen, attrListDbl);
|
2010-05-01 20:33:53 +00:00
|
|
|
|
2010-04-25 00:31:27 +00:00
|
|
|
if (vi == NULL)
|
2010-03-19 06:38:44 +00:00
|
|
|
{
|
|
|
|
vi = glXChooseVisual(glDisplay, glScreen, attrListSgl);
|
|
|
|
doubleBuffered = false;
|
2010-04-25 08:33:05 +00:00
|
|
|
ZZLog::Error_Log("Only Singlebuffered Visual!");
|
2010-03-19 02:04:55 +00:00
|
|
|
}
|
2010-04-25 00:31:27 +00:00
|
|
|
else
|
2010-03-19 06:38:44 +00:00
|
|
|
{
|
|
|
|
doubleBuffered = true;
|
2010-04-25 08:33:05 +00:00
|
|
|
ZZLog::Error_Log("Got Doublebuffered Visual!");
|
2010-03-19 02:04:55 +00:00
|
|
|
}
|
2010-05-01 20:33:53 +00:00
|
|
|
|
2010-04-25 00:31:27 +00:00
|
|
|
if (vi == NULL)
|
2010-04-08 21:58:14 +00:00
|
|
|
{
|
2010-04-25 08:33:05 +00:00
|
|
|
ZZLog::Error_Log("Failed to get buffered Visual!");
|
2010-04-08 21:58:14 +00:00
|
|
|
return false;
|
|
|
|
}
|
2010-09-01 10:28:37 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLWindow::GetWindowSize()
|
|
|
|
{
|
2010-09-05 15:47:09 +00:00
|
|
|
if (!glDisplay or !glWindow) return;
|
|
|
|
|
2010-09-01 10:28:37 +00:00
|
|
|
unsigned int borderDummy;
|
|
|
|
Window winDummy;
|
2010-09-05 15:47:09 +00:00
|
|
|
s32 xDummy;
|
|
|
|
s32 yDummy;
|
2010-09-01 10:28:37 +00:00
|
|
|
|
2010-09-06 22:21:07 +00:00
|
|
|
XLockDisplay(glDisplay);
|
2010-09-05 15:47:09 +00:00
|
|
|
XGetGeometry(glDisplay, glWindow, &winDummy, &xDummy, &yDummy, &width, &height, &borderDummy, &depth);
|
2010-09-06 22:21:07 +00:00
|
|
|
XUnlockDisplay(glDisplay);
|
2010-09-08 11:16:27 +00:00
|
|
|
|
|
|
|
// update the gl buffer size
|
2010-10-16 11:54:46 +00:00
|
|
|
ChangeWindowSize(width, height);
|
2010-09-08 11:16:27 +00:00
|
|
|
|
2010-10-21 11:13:49 +00:00
|
|
|
ZZLog::Dev_Log("Resolution %dx%d. Depth %d bpp. Position (%d,%d)", width, height, depth, conf.x, conf.y);
|
2010-09-01 10:28:37 +00:00
|
|
|
}
|
2010-03-19 02:04:55 +00:00
|
|
|
|
2010-09-01 10:28:37 +00:00
|
|
|
void GLWindow::GetGLXVersion()
|
|
|
|
{
|
|
|
|
int glxMajorVersion, glxMinorVersion;
|
|
|
|
|
2010-03-19 06:38:44 +00:00
|
|
|
glXQueryVersion(glDisplay, &glxMajorVersion, &glxMinorVersion);
|
2010-05-01 20:33:53 +00:00
|
|
|
|
2010-04-25 08:33:05 +00:00
|
|
|
ZZLog::Error_Log("glX-Version %d.%d", glxMajorVersion, glxMinorVersion);
|
2010-09-01 10:28:37 +00:00
|
|
|
}
|
|
|
|
|
2010-09-08 11:16:27 +00:00
|
|
|
void GLWindow::UpdateGrabKey()
|
|
|
|
{
|
2010-09-06 22:21:07 +00:00
|
|
|
// Do not stole the key in debug mode. It is not breakpoint friendly...
|
|
|
|
#ifndef _DEBUG
|
2010-09-05 16:30:47 +00:00
|
|
|
XLockDisplay(glDisplay);
|
2010-09-05 15:47:09 +00:00
|
|
|
if (fullScreen) {
|
|
|
|
XGrabPointer(glDisplay, glWindow, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, glWindow, None, CurrentTime);
|
|
|
|
XGrabKeyboard(glDisplay, glWindow, True, GrabModeAsync, GrabModeAsync, CurrentTime);
|
|
|
|
} else {
|
|
|
|
XUngrabPointer(glDisplay, CurrentTime);
|
|
|
|
XUngrabKeyboard(glDisplay, CurrentTime);
|
|
|
|
}
|
2010-09-05 16:30:47 +00:00
|
|
|
XUnlockDisplay(glDisplay);
|
2010-09-06 22:21:07 +00:00
|
|
|
#endif
|
2010-09-05 15:47:09 +00:00
|
|
|
}
|
|
|
|
|
2010-09-08 11:16:27 +00:00
|
|
|
void GLWindow::Force43Ratio()
|
|
|
|
{
|
|
|
|
// avoid black border in fullscreen
|
|
|
|
if (fullScreen && conf.isWideScreen) {
|
|
|
|
conf.width = width;
|
|
|
|
conf.height = height;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!fullScreen && !conf.isWideScreen) {
|
|
|
|
// Compute the width based on height
|
|
|
|
s32 new_width = (4*height)/3;
|
|
|
|
// do not bother to resize for 5 pixels. Avoid a loop
|
|
|
|
// due to round value
|
|
|
|
if ( abs(new_width - width) > 5) {
|
|
|
|
width = new_width;
|
|
|
|
conf.width = new_width;
|
|
|
|
// resize the window
|
|
|
|
XLockDisplay(glDisplay);
|
|
|
|
XResizeWindow(glDisplay, glWindow, new_width, height);
|
|
|
|
XSync(glDisplay, False);
|
|
|
|
XUnlockDisplay(glDisplay);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-05 15:47:09 +00:00
|
|
|
#define _NET_WM_STATE_REMOVE 0
|
|
|
|
#define _NET_WM_STATE_ADD 1
|
|
|
|
#define _NET_WM_STATE_TOGGLE 2
|
|
|
|
|
|
|
|
void GLWindow::ToggleFullscreen()
|
2010-09-01 10:28:37 +00:00
|
|
|
{
|
2010-09-05 15:47:09 +00:00
|
|
|
if (!glDisplay or !glWindow) return;
|
|
|
|
|
2010-09-08 11:16:27 +00:00
|
|
|
Force43Ratio();
|
2010-09-06 22:21:07 +00:00
|
|
|
|
2010-09-05 15:47:09 +00:00
|
|
|
u32 mask = SubstructureRedirectMask | SubstructureNotifyMask;
|
|
|
|
// Setup a new event structure
|
|
|
|
XClientMessageEvent cme;
|
|
|
|
cme.type = ClientMessage;
|
|
|
|
cme.send_event = True;
|
|
|
|
cme.display = glDisplay;
|
|
|
|
cme.window = glWindow;
|
|
|
|
cme.message_type = XInternAtom(glDisplay, "_NET_WM_STATE", False);
|
|
|
|
cme.format = 32;
|
2010-09-08 11:16:27 +00:00
|
|
|
// Note: can not use _NET_WM_STATE_TOGGLE because the WM can change the fullscreen state
|
|
|
|
// and screw up the fullscreen variable... The test on fulscreen restore a sane configuration
|
|
|
|
cme.data.l[0] = fullScreen ? _NET_WM_STATE_REMOVE : _NET_WM_STATE_ADD;
|
2010-09-05 15:47:09 +00:00
|
|
|
cme.data.l[1] = (u32)XInternAtom(glDisplay, "_NET_WM_STATE_FULLSCREEN", False);
|
|
|
|
cme.data.l[2] = 0;
|
|
|
|
cme.data.l[3] = 0;
|
|
|
|
|
|
|
|
// send the event
|
2010-09-05 16:30:47 +00:00
|
|
|
XLockDisplay(glDisplay);
|
2010-09-05 15:47:09 +00:00
|
|
|
if (!XSendEvent(glDisplay, RootWindow(glDisplay, vi->screen), False, mask, (XEvent*)(&cme)))
|
|
|
|
ZZLog::Error_Log("Failed to send event: toggle fullscreen");
|
2010-09-05 22:08:55 +00:00
|
|
|
else {
|
2010-09-05 15:47:09 +00:00
|
|
|
fullScreen = (!fullScreen);
|
2010-09-05 22:08:55 +00:00
|
|
|
conf.setFullscreen(fullScreen);
|
|
|
|
}
|
2010-09-05 16:30:47 +00:00
|
|
|
XUnlockDisplay(glDisplay);
|
2010-09-05 15:47:09 +00:00
|
|
|
|
|
|
|
// Apply the change
|
2010-10-15 09:32:41 +00:00
|
|
|
XSync(glDisplay, false);
|
|
|
|
|
|
|
|
// Wait a little that the VM does his joes. Actually the best is to check some WM event
|
|
|
|
// but it not sure it will appear so a time out is necessary.
|
|
|
|
usleep(100*1000); // 100 us should be far enough for old computer and unnoticeable for users
|
2010-09-05 15:47:09 +00:00
|
|
|
|
|
|
|
// update info structure
|
|
|
|
GetWindowSize();
|
|
|
|
|
|
|
|
UpdateGrabKey();
|
|
|
|
|
2010-09-08 19:04:11 +00:00
|
|
|
// avoid black border in widescreen fullscreen
|
|
|
|
if (fullScreen && conf.isWideScreen) {
|
|
|
|
conf.width = width;
|
|
|
|
conf.height = height;
|
|
|
|
}
|
2010-09-08 11:16:27 +00:00
|
|
|
|
2010-09-05 15:47:09 +00:00
|
|
|
// Hide the cursor in the right bottom corner
|
|
|
|
if(fullScreen)
|
2010-10-15 09:32:41 +00:00
|
|
|
XWarpPointer(glDisplay, None, glWindow, 0, 0, 0, 0, 2*width, 2*height);
|
2010-09-05 15:47:09 +00:00
|
|
|
|
|
|
|
}
|
2010-09-01 10:28:37 +00:00
|
|
|
|
|
|
|
bool GLWindow::DisplayWindow(int _width, int _height)
|
|
|
|
{
|
|
|
|
if (!CreateVisual()) return false;
|
|
|
|
|
2010-03-19 02:04:55 +00:00
|
|
|
/* create a GLX context */
|
2010-04-08 21:58:14 +00:00
|
|
|
context = glXCreateContext(glDisplay, vi, NULL, GL_TRUE);
|
2010-09-01 10:28:37 +00:00
|
|
|
|
2010-03-19 02:04:55 +00:00
|
|
|
/* create a color map */
|
2010-09-06 09:46:33 +00:00
|
|
|
attr.colormap = XCreateColormap(glDisplay, RootWindow(glDisplay, vi->screen),
|
2010-03-19 02:04:55 +00:00
|
|
|
vi->visual, AllocNone);
|
2010-03-19 06:38:44 +00:00
|
|
|
attr.border_pixel = 0;
|
2010-09-05 15:47:09 +00:00
|
|
|
attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask |
|
|
|
|
StructureNotifyMask | SubstructureRedirectMask | SubstructureNotifyMask |
|
|
|
|
EnterWindowMask | LeaveWindowMask | FocusChangeMask ;
|
2010-03-19 02:04:55 +00:00
|
|
|
|
2010-09-01 10:28:37 +00:00
|
|
|
GetGLXVersion();
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2010-09-06 12:23:45 +00:00
|
|
|
// Create a window at the last position/size
|
2010-09-05 15:47:09 +00:00
|
|
|
glWindow = XCreateWindow(glDisplay, RootWindow(glDisplay, vi->screen),
|
2010-09-06 12:23:45 +00:00
|
|
|
conf.x , conf.y , _width, _height, 0, vi->depth, InputOutput, vi->visual,
|
2010-09-05 15:47:09 +00:00
|
|
|
CWBorderPixel | CWColormap | CWEventMask,
|
|
|
|
&attr);
|
|
|
|
|
|
|
|
/* Allow to kill properly the window */
|
|
|
|
Atom wmDelete = XInternAtom(glDisplay, "WM_DELETE_WINDOW", True);
|
|
|
|
XSetWMProtocols(glDisplay, glWindow, &wmDelete, 1);
|
|
|
|
|
|
|
|
// Set icon name
|
|
|
|
XSetIconName(glDisplay, glWindow, "ZZogl-pg");
|
|
|
|
|
|
|
|
// Draw the window
|
|
|
|
XMapRaised(glDisplay, glWindow);
|
|
|
|
XSync(glDisplay, false);
|
2010-03-19 02:04:55 +00:00
|
|
|
|
|
|
|
// connect the glx-context to the window
|
2010-03-19 06:38:44 +00:00
|
|
|
glXMakeCurrent(glDisplay, glWindow, context);
|
2010-09-01 10:28:37 +00:00
|
|
|
|
2010-04-25 00:31:27 +00:00
|
|
|
if (glXIsDirect(glDisplay, context))
|
2010-04-25 08:33:05 +00:00
|
|
|
ZZLog::Error_Log("You have Direct Rendering!");
|
2010-03-19 02:04:55 +00:00
|
|
|
else
|
2010-04-25 08:33:05 +00:00
|
|
|
ZZLog::Error_Log("No Direct Rendering possible!");
|
2010-03-19 02:04:55 +00:00
|
|
|
|
2010-09-08 11:16:27 +00:00
|
|
|
// Always start in window mode
|
2010-09-05 22:08:55 +00:00
|
|
|
fullScreen = 0;
|
2010-09-08 11:16:27 +00:00
|
|
|
GetWindowSize();
|
2010-03-19 05:24:36 +00:00
|
|
|
|
|
|
|
return true;
|
2010-03-19 02:04:55 +00:00
|
|
|
}
|
|
|
|
|
2010-03-19 05:24:36 +00:00
|
|
|
void GLWindow::SwapGLBuffers()
|
2010-03-19 02:04:55 +00:00
|
|
|
{
|
2010-05-01 20:33:53 +00:00
|
|
|
glXSwapBuffers(glDisplay, glWindow);
|
2010-09-15 16:54:19 +00:00
|
|
|
//glClear(GL_COLOR_BUFFER_BIT);
|
2010-03-19 02:04:55 +00:00
|
|
|
}
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2010-03-19 02:04:55 +00:00
|
|
|
void GLWindow::SetTitle(char *strtitle)
|
|
|
|
{
|
2010-09-05 16:30:47 +00:00
|
|
|
if (!glDisplay or !glWindow) return;
|
|
|
|
if (fullScreen) return;
|
2010-05-01 20:33:53 +00:00
|
|
|
|
2010-09-05 16:30:47 +00:00
|
|
|
XTextProperty prop;
|
|
|
|
memset(&prop, 0, sizeof(prop));
|
2010-05-01 20:33:53 +00:00
|
|
|
|
2010-09-05 16:30:47 +00:00
|
|
|
char* ptitle = strtitle;
|
|
|
|
if (XStringListToTextProperty(&ptitle, 1, &prop)) {
|
|
|
|
XLockDisplay(glDisplay);
|
|
|
|
XSetWMName(glDisplay, glWindow, &prop);
|
|
|
|
XUnlockDisplay(glDisplay);
|
|
|
|
}
|
|
|
|
|
|
|
|
XFree(prop.value);
|
2010-03-19 02:04:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GLWindow::ResizeCheck()
|
|
|
|
{
|
2010-05-01 20:33:53 +00:00
|
|
|
XEvent event;
|
2010-09-06 22:21:07 +00:00
|
|
|
if (!glDisplay or !glWindow) return;
|
2010-05-01 20:33:53 +00:00
|
|
|
|
2010-09-06 22:21:07 +00:00
|
|
|
XLockDisplay(glDisplay);
|
|
|
|
while (XCheckTypedWindowEvent(glDisplay, glWindow, ConfigureNotify, &event))
|
2010-05-01 20:33:53 +00:00
|
|
|
{
|
|
|
|
if ((event.xconfigure.width != width) || (event.xconfigure.height != height))
|
|
|
|
{
|
|
|
|
width = event.xconfigure.width;
|
|
|
|
height = event.xconfigure.height;
|
2010-09-08 11:16:27 +00:00
|
|
|
Force43Ratio();
|
2010-10-16 11:54:46 +00:00
|
|
|
ChangeWindowSize(width, height);
|
2010-05-01 20:33:53 +00:00
|
|
|
}
|
|
|
|
|
2010-09-05 15:47:09 +00:00
|
|
|
if (!fullScreen) {
|
2010-09-06 09:46:33 +00:00
|
|
|
if ((event.xconfigure.x != conf.x) || (event.xconfigure.y != conf.y))
|
2010-09-05 15:47:09 +00:00
|
|
|
{
|
|
|
|
// Fixme; x&y occassionally gives values near the top left corner rather then the real values,
|
|
|
|
// causing the window to change positions when adjusting ZZOgl's settings.
|
2010-09-06 09:46:33 +00:00
|
|
|
conf.x = event.xconfigure.x;
|
|
|
|
conf.y = event.xconfigure.y;
|
2010-09-05 15:47:09 +00:00
|
|
|
}
|
|
|
|
}
|
2010-05-01 20:33:53 +00:00
|
|
|
}
|
2010-09-06 22:21:07 +00:00
|
|
|
XUnlockDisplay(glDisplay);
|
2010-03-19 02:04:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|