I have read and pieced multiple projects together in order to create an x11 window with open gl working, with the preinstalled GL/gl.h and GL/glx.h. The problem I get is that the triangles I want to draw to the screen does not show. What I think is that I have not setup any projecting parameters etc, or that the triangles doesn't get drawn to the space I want to draw to.
I do get a window and I am able to setup xevents that triggers after I have subscribed with eventmasks. Pressing 'esc' key will trigger an event which will in the end call Shutdown()
and break the loop, free up x11 and gl stuff, and lastly exit the program. So the only thing that doesn't work is the drawing to screen stuff which basically is the main point of my program.
How can I resolve this?
main.cpp:
#include <cstring>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysymdef.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <sys/time.h>
#include <unistd.h>
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
#define FPS 30
#define TEST_LOCAL
int shutdown = 0;
extern bool Initialize(int w, int h);
extern bool Update(float deltaTime);
extern void Render();
extern void HandleKeyboardEvents( XEvent );
extern void Resize(int w, int h);
extern void Shutdown();
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
#define SKIP_TICKS (1000 / FPS)
static double GetMilliseconds() {
static timeval s_tTimeVal;
gettimeofday(&s_tTimeVal, NULL);
double time = s_tTimeVal.tv_sec * 1000.0; // sec to ms
time += s_tTimeVal.tv_usec / 1000.0; // us to ms
return time;
}
static bool isExtensionSupported(const char *extList, const char *extension) {
return strstr(extList, extension) != 0;
}
int main(int argc, char** argv) {
Display* display;
Window window;
Screen* screen;
int screenId;
XEvent ev;
// Open the display
display = XOpenDisplay(NULL);
if (display == NULL) {
std::cout << "Could not open display\n";
return 1;
}
screen = DefaultScreenOfDisplay(display);
screenId = DefaultScreen(display);
// Check GLX version
GLint majorGLX, minorGLX = 0;
glXQueryVersion(display, &majorGLX, &minorGLX);
if (majorGLX <= 1 && minorGLX < 2) {
std::cout << "GLX 1.2 or greater is required.\n";
XCloseDisplay(display);
return 1;
}
GLint glxAttribs[] = {
GLX_X_RENDERABLE , True,
GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT,
GLX_RENDER_TYPE , GLX_RGBA_BIT,
GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR,
GLX_RED_SIZE , 8,
GLX_GREEN_SIZE , 8,
GLX_BLUE_SIZE , 8,
GLX_ALPHA_SIZE , 8,
GLX_DEPTH_SIZE , 24,
GLX_STENCIL_SIZE , 8,
GLX_DOUBLEBUFFER , True,
None
};
int fbcount;
GLXFBConfig* fbc = glXChooseFBConfig(display, screenId, glxAttribs, &fbcount);
if (fbc == 0) {
std::cout << "Failed to retrieve framebuffer.\n";
XCloseDisplay(display);
return 1;
}
// Pick the FB config/visual with the most samples per pixel
int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999;
for (int i = 0; i < fbcount; ++i) {
XVisualInfo *vi = glXGetVisualFromFBConfig( display, fbc[i] );
if ( vi != 0) {
int samp_buf, samples;
glXGetFBConfigAttrib( display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf );
glXGetFBConfigAttrib( display, fbc[i], GLX_SAMPLES , &samples );
if ( best_fbc < 0 || (samp_buf && samples > best_num_samp) ) {
best_fbc = i;
best_num_samp = samples;
}
if ( worst_fbc < 0 || !samp_buf || samples < worst_num_samp )
worst_fbc = i;
worst_num_samp = samples;
}
XFree( vi );
}
GLXFBConfig bestFbc = fbc[ best_fbc ];
XFree( fbc ); // Make sure to free this!
XVisualInfo* visual = glXGetVisualFromFBConfig( display, bestFbc );
if (visual == 0) {
std::cout << "Could not create correct visual window.\n";
XCloseDisplay(display);
return 1;
}
if (screenId != visual->screen) {
std::cout << "screenId(" << screenId << ") does not match visual->screen(" << visual->screen << ").\n";
XCloseDisplay(display);
return 1;
}
// Open the window
XSetWindowAttributes windowAttribs;
windowAttribs.border_pixel = BlackPixel(display, screenId);
windowAttribs.background_pixel = WhitePixel(display, screenId);
windowAttribs.override_redirect = True;
windowAttribs.colormap = XCreateColormap(display, RootWindow(display, screenId), visual->visual, AllocNone);
windowAttribs.event_mask = ExposureMask;
window = XCreateWindow(display, RootWindow(display, screenId), 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, visual->depth, InputOutput, visual->visual, CWBackPixel | CWColormap | CWBorderPixel | CWEventMask, &windowAttribs);
// Redirect Close
Atom atomWmDeleteWindow = XInternAtom(display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, window, &atomWmDeleteWindow, 1);
// Create GLX OpenGL context
glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" );
int context_attribs[] = {
GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
GLX_CONTEXT_MINOR_VERSION_ARB, 2,
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
None
};
GLXContext context = 0;
const char *glxExts = glXQueryExtensionsString( display, screenId );
if (!isExtensionSupported( glxExts, "GLX_ARB_create_context")) {
std::cout << "GLX_ARB_create_context not supported\n";
context = glXCreateNewContext( display, bestFbc, GLX_RGBA_TYPE, 0, True );
}
else {
context = glXCreateContextAttribsARB( display, bestFbc, 0, true, context_attribs );
}
XSync( display, False );
// Verifying that context is a direct context
if (!glXIsDirect (display, context)) {
std::cout << "Indirect GLX rendering context obtained\n";
}
else {
std::cout << "Direct GLX rendering context obtained\n";
}
glXMakeCurrent(display, window, context);
std::cout << "GL Renderer: " << glGetString(GL_RENDERER) << "\n";
std::cout << "GL Version: " << glGetString(GL_VERSION) << "\n";
std::cout << "GLSL Version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << "\n";
if (!Initialize(WINDOW_WIDTH, WINDOW_HEIGHT)) {
glXDestroyContext(display, context);
XFree(visual);
XFreeColormap(display, windowAttribs.colormap);
XDestroyWindow(display, window);
XCloseDisplay(display);
return 1;
}
glMatrixMode( GL_PROJECTION );
glOrtho( 0, 640, 480, 0, -1, 1 );
XSelectInput( display, window, PointerMotionMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask );
// Show the window
XClearWindow(display, window);
XMapRaised(display, window);
double prevTime = GetMilliseconds();
double currentTime = GetMilliseconds();
double deltaTime = 0.0;
timeval time;
long sleepTime = 0;
gettimeofday(&time, NULL);
long nextGameTick = (time.tv_sec * 1000) + (time.tv_usec / 1000);
// Enter message loop
while (shutdown != 1) {
if (XPending(display) > 0) {
XNextEvent(display, &ev);
if (ev.type == Expose) {
XWindowAttributes attribs;
XGetWindowAttributes(display, window, &attribs);
Resize(attribs.width, attribs.height);
}
if (ev.type == ClientMessage) {
if (ev.xclient.data.l[0] == atomWmDeleteWindow) {
break;
}
}
else if (ev.type == DestroyNotify) {
break;
}
}
currentTime = GetMilliseconds();
deltaTime = double(currentTime - prevTime) * 0.001;
prevTime = currentTime;
if (!Update((float)deltaTime)) {
break;
}
HandleKeyboardEvents( ev );
Render();
// Present frame
glXSwapBuffers(display, window);
// Limit Framerate
gettimeofday(&time, NULL);
nextGameTick += SKIP_TICKS;
sleepTime = nextGameTick - ((time.tv_sec * 1000) + (time.tv_usec / 1000));
usleep((unsigned int)(sleepTime / 1000));
}
std::cout << "Shutting Down\n";
Shutdown();
// Cleanup GLX
glXDestroyContext(display, context);
// Cleanup X11
XFree(visual);
XFreeColormap(display, windowAttribs.colormap);
XDestroyWindow(display, window);
XCloseDisplay(display);
return 0;
}
#ifdef TEST_LOCAL
bool Initialize(int w, int h) {
glClearColor(0.5f, 0.6f, 0.7f, 1.0f);
glViewport(0, 0, w, h);
return true;
}
bool Update(float deltaTime) {
return true;
}
void HandleKeyboardEvents( XEvent ev ) {
int x, y;
switch ( ev.type ) {
case ButtonPress:
if ( ev.xbutton.button == 1 ) {
std::cout << "Left mouse down \n";
}
break;
case ButtonRelease:
if ( ev.xbutton.button == 1 ) {
std::cout << "Left mouse up \n";
}
break;
case KeyPress:
if ( ev.xkey.keycode == 9 ) { // ESC
Shutdown();
}
break;
case MotionNotify:
x = ev.xmotion.x;
y = ev.xmotion.y;
//std::cout << "Mouse X:" << x << ", Y: " << y << "\n";
break;
}
}
void Render() {
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f( 1.0f, 0.0f, 0.0f);
glVertex3f( 0.0f, -1.0f, 0.0f);
glColor3f( 1.0f, 0.0f, 0.0f);
glVertex3f(-1.0f, 1.0f, 0.0f);
glColor3f( 1.0f, 0.0f, 0.0f);
glVertex3f( 1.0f, 1.0f, 0.0f);
glEnd();
glFlush();
}
void Resize(int w, int h) {
glViewport(0, 0, w, h);
}
void Shutdown() {
shutdown = 1;
}
#endif
I compile with:
g++ -g -Wall -o _build/main main.cpp -I/opt/x11/include -L/usr/x11/lib -lGL -lX11
OS:
Linux kali 5.9.0-kali4-amd64 #1 SMP Debian 5.9.11-1kali1 (2020-12-01) x86_64 GNU/Linux
Your code will not render the triangle, but will generate GL_INVALID_OPERATION
on your glBegin
/glEnd
construct instead. The reason lies here:
int context_attribs[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, 2, GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, None };
According to the GLX_ARB_create_context
GLX extension spec (emphasis mine):
If version 3.2 or greater is requested, the context returned may implement any of the following versions:
- The requested profile of the requested version.
- The requested profile of any later version, so long as no features have been removed from that later version and profile.
with the profile being requested via the GLX_CONTEXT_PROFILE_MASK_ARB
attribute, which you ommitted, so the default will apply:
The default value for
GLX_CONTEXT_PROFILE_MASK_ARB
isGLX_CONTEXT_CORE_PROFILE_BIT_ARB
. All OpenGL 3.2 implementations are required to implement the core profile, but implementation of the compatibility profile is optional.
The legacy features like glBegin
/glEnd
drawing mode, the fixed-function pipeline, the matrix stack are all removed from core profiles of OpenGL.
To get this code to run, you have 3 options:
GLX_ARB_create_context
extension at all.GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB
(and remove the GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB
). This might or might not work depending on wether your implementation supports.Option 3 is actually the only one which makes sense in the year 2021. However, it will require much more setup code than you currently have. You will have to write your own shaders, and set up a VAO together with a VBO for the vertex attributes and the respective data arrays.
Note that option 3 will also break the
with the preinstalled GL/gl.h and GL/glx.h.
part of your premise. You'll want to drop at least the gl.h
header in favor of one generated by a GL loader.