├── LICENSE ├── README.md ├── Sources.zip └── X11.cpp /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Introduction 2 | Most tutorials seem to start off with a discussion of how hard X is, how it will mess up your head and how only a masochist would ever write X code with alternates such as SDL out there. This, in my opinion is just wrong. In the trinity of major operating systems i think X is the sanest, most reliable and cleanest designed window manager. With that in mind, lets create an OpenGL enabled X11 window for video games. 3 | 4 | #Part 1, Creating a Window 5 | The X windowing system uses a client/server architecture. A single machine can have X running in multiple instances. 6 | 7 | ##Creating an X Window 8 | The first thing to do when opening a window under x is to tell it where the screen is. Altough there are several ways of telling the client where the server is, the most fullproof is the **DISPLAY** environment variable, or using *NULL* for default. 9 | 10 | The function ```XOpenDisplay(char* display)``` makes the connection to the X server. It takes one argument, a string using the display format described above, or *NULL* to use the default. It returns a pointer to the display (of type *Display*) on success or *NULL* on error. 11 | 12 | With the pointer the display, find the screen using the ```DefaultScreenOfDisplay(Display* display)``` function. This function takes one parameter, the pointer to the *Display* object that was found using **XOpenDisplay**. **DefaultScreenOfDisplay** returns a pointer of type *Screen* on success, *NULL* on error. 13 | 14 | To find the screen ID of the screen use the ```DefaultScreen(Display* display)``` function, this is almost identical to **DefaultScreenOfDisplay** except that it retuns an integer. 15 | 16 | With just a display and a screen a window can be created with the **XCreateSimpleWindow** function. The prototype for this function is as follows 17 | ``` 18 | Window XCreateSimpleWindow(Display* display, Window* parent, int x, int y, uint borderw, ulong border, ulong background); 19 | ``` 20 | The first argument *display* is a pointer to the display object acquired using **XOpenDisplay**. The next argument, parent is a pointer to the window opening this one. If this is the first window of a program opening, use the ```RootWindowOfScreen(Screen* screen)``` function. This function takes one argument, the pointer to the screen that was acquired using **DefaultScreenOfDisplay** 21 | 22 | The next four arguments are the location and sizeof the window. the *border-width* argument specifies the width of the window border in pixels. The next argument *border* is the color of the border. The last paramater *background* is simply the background color of the window. 23 | 24 | There are several utility functions to acquire system colors (of type ulong). ```BlackPixel(Display* display, int screenId)``` and ```WhitePixel(Display* display, int screenId)``` are great examples. All of these functions take two arguments, a pointer to the display to use and the screenId being used. 25 | 26 | The next step is to clear the window using ```XClearWindow(Display* display, Window* window)```, this function takes two arguments the display and the window to clear. Finally raise the window using the ```XMapRaised(Display* display, Window* window)``` function, which uses the same arguments as **XClearWindow** 27 | 28 | At this point the window will not show up yet. First it must process messages (such as show window) that are sent to it. To process messages, start a "message loop", in it call ```XNextEvent(Display* display, XEvent* e)```. This function will return the current active event, or block until the next event is recieved. It take a *Display* object as it's first argument and writes to an *XEvent* pointer that is it's second argument. 29 | 30 | After the window is finished, force the window to close with the ```XDestroyWindow(Display* display, Window window)``` function and cleanup the display using the ```XCloseDisplay(Display* display)``` function, which takes one argument, the display to close. 31 | 32 | ###Sample Code (C) 33 | ``` 34 | #include 35 | 36 | int main(int argc, char** argv) { 37 | Display* display; 38 | Window window; 39 | Screen* screen; 40 | int screenId; 41 | XEvent ev; 42 | 43 | // Open the display 44 | display = XOpenDisplay(NULL); 45 | if (display == NULL) { 46 | printf("%s\n", "Could not open display"); 47 | return 1; 48 | } 49 | screen = DefaultScreenOfDisplay(display); 50 | screenId = DefaultScreen(display); 51 | 52 | // Open the window 53 | window = XCreateSimpleWindow(display, RootWindowOfScreen(screen), 0, 0, 320, 200, 1, BlackPixel(display, screenId), WhitePixel(display, screenId)); 54 | 55 | // Show the window 56 | XClearWindow(display, window); 57 | XMapRaised(display, window); 58 | 59 | // Enter message loop 60 | while (true) { 61 | XNextEvent(display, &ev); 62 | // Do something 63 | } 64 | 65 | // Cleanup 66 | XDestroyWindow(display, window); 67 | XFree(screen); 68 | XCloseDisplay(display); 69 | return 1; 70 | } 71 | ``` 72 | Compile the program using the following command line 73 | ``` 74 | LINUX: gcc -o XSampleWindow XSampleWindow.c -lX11 -L/usr/X11R6/lib -I/usr/X11R6/include 75 | 76 | OSX: gcc -o XSampleWindow XSampleWindow.c -lX11 -L/usr/X11/lib -I/opt/X11/include 77 | ``` 78 | 79 | #Part 2, Processing Events 80 | To register for events use the ```XSelectInput(Display* display, uint mask)``` function, it takes as arguments the display object, the window object and a bitmask representing what events the message loop will subscribe to. This function should be called before the *XMapWindow* function. 81 | 82 | TODO: Event table listing 83 | http://tronche.com/gui/x/xlib/events/processing-overview.html 84 | 85 | ##Keyboard Input 86 | To process keyboard input subscribe to three event masks *KeyPressMask* *KeyReleaseMask* and *KeymapStateMask*. The first two events will fire when a key is pressed or released. X handles mapping characters to key numbers, this mapping can change at any time if the user remaps keys. the KeyMask event is fired when the keymapping of the system changes. 87 | 88 | Once subscribed to ```KeyPressMask | KeyReleaseMask | KeymapStateMask``` three events types will be fired, they are *KeyPress*, *KeyRelease* and *KeymapNotify*. Check the *type* variable of the *XEvent* object against these types. 89 | 90 | In case the *KeyMapNotify* event is recieved, call the ```XRefreshKeyboardMapping(XMappingEvent* event_map);``` function. The function takes one argument, a *XMappingEvent* pointer. 91 | 92 | If the *KeyPress* or *KeyRelease* events get fired use the ```int XLookupString(XKeyEvent *event_struct, char *buffer_return, int bytes_buffer, KeySym *keysym_return, XComposeStatus *status_in_out)``` function to translate the event into a **KeySym** (a *KeySym* is a typedef for an unsigned long coresponding to the keyID of the key pressed) and a string. *buffer_return* will hold the translated character string, *keysym_return* will hold the *KeySym* value. **keysymdef.h** holds a list of #define strings for each key. 93 | 94 | ###Sample Code (C++) 95 | ``` 96 | #include 97 | #include 98 | #include 99 | #include 100 | 101 | int main(int argc, char** argv) { 102 | Display* display; 103 | Window window; 104 | Screen* screen; 105 | int screenId; 106 | XEvent ev; 107 | 108 | // Open the display 109 | display = XOpenDisplay(NULL); 110 | if (display == NULL) { 111 | printf("%s\n", "Could not open display"); 112 | return 1; 113 | } 114 | screen = DefaultScreenOfDisplay(display); 115 | screenId = DefaultScreen(display); 116 | 117 | // Open the window 118 | window = XCreateSimpleWindow(display, RootWindowOfScreen(screen), 0, 0, 320, 200, 1, BlackPixel(display, screenId), WhitePixel(display, screenId)); 119 | 120 | XSelectInput(display, window, KeyPressMask | KeyReleaseMask | KeymapStateMask); 121 | 122 | // Show the window 123 | XClearWindow(display, window); 124 | XMapRaised(display, window); 125 | 126 | // Variables used in message loop 127 | char str[25] = {0}; 128 | KeySym keysym = 0; 129 | int len = 0; 130 | bool running = true; 131 | 132 | // Enter message loop 133 | while (running) { 134 | XNextEvent(display, &ev); 135 | switch(ev.type) { 136 | case KeymapNotify: 137 | XRefreshKeyboardMapping(&ev.xmapping); 138 | break; 139 | case KeyPress: 140 | len = XLookupString(&ev.xkey, str, 25, &keysym, NULL); 141 | if (len > 0) { 142 | std::cout << "Key pressed: " << str << " - " << len << " - " << keysym <<'\n'; 143 | } 144 | if (keysym == XK_Escape) { 145 | running = false; 146 | } 147 | break; 148 | case KeyRelease: 149 | len = XLookupString(&ev.xkey, str, 25, &keysym, NULL); 150 | if (len > 0) { 151 | std::cout << "Key released: " << str << " - " << len << " - " << keysym <<'\n'; 152 | } 153 | break; 154 | } 155 | } 156 | 157 | // Cleanup 158 | XDestroyWindow(display, window); 159 | XCloseDisplay(display); 160 | return 1; 161 | } 162 | ``` 163 | Compile the program using the following command line 164 | ``` 165 | LINUX: g++ -o XKeyIn XKeyIn.cpp -lX11 -L/usr/X11R6/lib -I/usr/X11R6/include 166 | 167 | OSX: g++ -o XKeyIn XKeyIn.cpp -lX11 -L/usr/X11/lib -I/opt/X11/include 168 | ``` 169 | 170 | ##Mouse Input 171 | To process mouse events subscribe to the *PointerMotionMask*, *ButtonPressMask*, *ButtonReleaseMask*, *EnterWindowMask* and *LeaveWindowMask* event masks. These masks will fire the following events: *MotionNotify*, *ButtonPress*, *ButtonRelease*, *EnterNotify* and *LeaveNotify*. 172 | 173 | Masks: 174 | ```PointerMotionMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask``` 175 | 176 | On *ButtonPress* and *ButtonRelease* the event object will contains an *xbutton* field. This field has a button variable, which maps as follows: 1 - left mouse, 2 - middle mouse, 3 - right mouse, 4 - scroll up, 5 - scroll down. 177 | 178 | On *MotionNotify* the event object will contain a *xmotion* field. This field has two variables *x* and *y*. The *x* and *y* variables contain the mouses position in window space. 179 | 180 | ###Sample Code (C++) 181 | ``` 182 | #include 183 | #include 184 | #include 185 | #include 186 | 187 | int main(int argc, char** argv) { 188 | Display* display; 189 | Window window; 190 | Screen* screen; 191 | int screenId; 192 | XEvent ev; 193 | 194 | // Open the display 195 | display = XOpenDisplay(NULL); 196 | if (display == NULL) { 197 | printf("%s\n", "Could not open display"); 198 | return 1; 199 | } 200 | screen = DefaultScreenOfDisplay(display); 201 | screenId = DefaultScreen(display); 202 | 203 | // Open the window 204 | window = XCreateSimpleWindow(display, RootWindowOfScreen(screen), 0, 0, 320, 200, 1, BlackPixel(display, screenId), WhitePixel(display, screenId)); 205 | 206 | XSelectInput(display, window, PointerMotionMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask); 207 | 208 | // Show the window 209 | XClearWindow(display, window); 210 | XMapRaised(display, window); 211 | 212 | // Variables used in message loop 213 | bool running = true; 214 | int x, y; 215 | 216 | // Enter message loop 217 | while (running) { 218 | XNextEvent(display, &ev); 219 | switch(ev.type) { 220 | case ButtonPress: 221 | if (ev.xbutton.button == 1) { 222 | std::cout << "Left mouse down\n"; 223 | } 224 | else if (ev.xbutton.button == 2) { 225 | std::cout << "Middle mouse down\n"; 226 | } 227 | else if (ev.xbutton.button == 3) { 228 | std::cout << "Right mouse down\n"; 229 | } 230 | else if (ev.xbutton.button == 4) { 231 | std::cout << "Mouse scroll up\n"; 232 | } 233 | else if (ev.xbutton.button == 5) { 234 | std::cout << "Mouse scroll down\n"; 235 | } 236 | break; 237 | case ButtonRelease: 238 | if (ev.xbutton.button == 1) { 239 | std::cout << "Left mouse up\n"; 240 | } 241 | else if (ev.xbutton.button == 2) { 242 | std::cout << "Middle mouse up\n"; 243 | } 244 | else if (ev.xbutton.button == 3) { 245 | std::cout << "Right mouse up\n"; 246 | running = false; 247 | } 248 | break; 249 | case MotionNotify: 250 | x = ev.xmotion.x; 251 | y = ev.xmotion.y; 252 | std::cout << "Mouse X:" << x << ", Y: " << y << "\n"; 253 | break; 254 | case EnterNotify: 255 | std::cout << "Mouse enter\n"; 256 | break; 257 | case LeaveNotify: 258 | std::cout << "Mouse leave\n"; 259 | break; 260 | } 261 | } 262 | 263 | // Cleanup 264 | XDestroyWindow(display, window); 265 | XCloseDisplay(display); 266 | return 1; 267 | } 268 | ``` 269 | 270 | ##Window Size & Title 271 | Set the window's title using the ```XStoreName(Display* d, Window w, char *window_name)``` function. The first argument is the display link, the second argument is the window object and the third argument is the new window title. 272 | 273 | Query the size of the current window with the ```XGetWindowAttributes(Display* d, Window w, XWindowAttributes* winAttribs);``` function. The first argument is the display link, the second is the window object. The third argument is a *XWindowAttributes* object, this object has two fields *width* and *height*. 274 | 275 | When a window is resized the *Expose* event is fired, listen for this event by subscribing the *ExposureMask* event mask. When processing the *Expose* event, query the window size with the *XGetWindowAttributes* funcion described above. 276 | 277 | At runtime the width, height and position of the window can be changed with the ```XConfigureWindow(Display* d, Window w, uint value_mask, XWindowChanges *values;``` function. The first argument is the display link, the second argument is the window. The third argument is an unsigned int, this is a bitmask representing which fields of the fourth argument to use. The fourth argument is a pointer to a *XWindowChanges* structure. Valid values for the mask are: *CWX*, *CWY*, *CWWidth*, *CWHeight*, *CWBorderWidth*, *CWSibling* and *CWStackMode*. The *XWindowChanges* struct has the following fields: ```int x, y```, ```int width, height```, ```int border_width```, ```Window sibling``` and ```int stack_mode```. 278 | 279 | ###Sample Code (C++) 280 | ``` 281 | #include 282 | #include 283 | #include 284 | #include 285 | 286 | int main(int argc, char** argv) { 287 | Display* display; 288 | Window window; 289 | Screen* screen; 290 | int screenId; 291 | XEvent ev; 292 | 293 | // Open the display 294 | display = XOpenDisplay(NULL); 295 | if (display == NULL) { 296 | printf("%s\n", "Could not open display"); 297 | return 1; 298 | } 299 | screen = DefaultScreenOfDisplay(display); 300 | screenId = DefaultScreen(display); 301 | 302 | // Open the window 303 | window = XCreateSimpleWindow(display, RootWindowOfScreen(screen), 0, 0, 320, 200, 1, BlackPixel(display, screenId), WhitePixel(display, screenId)); 304 | 305 | XSelectInput(display, window, ExposureMask); 306 | 307 | // Name the window 308 | XStoreName(display, window, "Named Window"); 309 | 310 | // Show the window 311 | XClearWindow(display, window); 312 | XMapRaised(display, window); 313 | 314 | // How large is the window 315 | XWindowAttributes attribs; 316 | XGetWindowAttributes(display, window, &attribs); 317 | std::cout << "Window Width: " << attribs.width << ", Height: " << attribs.height << "\n"; 318 | 319 | // Resize window 320 | unsigned int change_values = CWWidth | CWHeight; 321 | XWindowChanges values; 322 | values.width = 800; 323 | values.height = 600; 324 | XConfigureWindow(display, window, change_values, &values); 325 | 326 | // Variables used in message loop 327 | bool running = true; 328 | int x, y; 329 | 330 | // Enter message loop 331 | while (running) { 332 | XNextEvent(display, &ev); 333 | switch(ev.type) { 334 | case Expose: 335 | std::cout << "Expose event fired\n"; 336 | XGetWindowAttributes(display, window, &attribs); 337 | std::cout << "\tWindow Width: " << attribs.width << ", Height: " << attribs.height << "\n"; 338 | break; 339 | } 340 | } 341 | 342 | // Cleanup 343 | XDestroyWindow(display, window); 344 | XCloseDisplay(display); 345 | return 1; 346 | } 347 | ``` 348 | 349 | #Part 3, OpenGL 350 | OpenGL on X11 is done trough the X11 library, include the `````` header. 351 | ##Enabling OpenGL 352 | Before showing the window with *XMapRaised* it's a good idea to check the version of GLX available. Note, the **GLX version is not the same as the OpenGL version**. This can be done with the ```glXQueryVersion(Display* display, GLint* major, GLint* minor)``` function. The first argument is a pointer to the display object, the second two arguments are integer pointer that will have the major and minor version number written to them. If the minimum version of OpenGL is not supported, exit the program. A good minimum verions is 1.2 353 | ``` 354 | GLint majorGLX, minorGLX = 0; 355 | glXQueryVersion(display, &majorGLX, &minorGLX); 356 | if (majorGLX <= 1 && minorGLX < 2) { 357 | std::cout << "GLX 1.2 or greater is required.\n"; 358 | XCloseDisplay(display); 359 | return 1; 360 | } 361 | else { 362 | std::cout << "GLX version: " << majorGLX << "." << minorGLX << '\n'; 363 | } 364 | ``` 365 | To create an OpenGL window a *XVisualInfo* object is needed. This object describes the minimum requirements to create a window. It is created with the ```glXChooseVisual(Display *display, int screen, int *attribList)``` function. If a window matching these minimum requirements can not be created *NULL* is returned. The first argument is the display object, the second is the screen id. The third argument is an array of flags used for creating the window. The creation code below makes a double buffered window with a 24 bit depth and 8bit stencil buffer. 366 | ``` 367 | GLint glxAttribs[] = { 368 | GLX_RGBA, 369 | GLX_DOUBLEBUFFER, 370 | GLX_DEPTH_SIZE, 24, 371 | GLX_STENCIL_SIZE, 8, 372 | GLX_RED_SIZE, 8, 373 | GLX_GREEN_SIZE, 8, 374 | GLX_BLUE_SIZE, 8, 375 | GLX_SAMPLE_BUFFERS, 0, 376 | GLX_SAMPLES, 0, 377 | None 378 | }; 379 | XVisualInfo* visual = glXChooseVisual(display, screenId, glxAttribs); 380 | 381 | if (visual == 0) { 382 | std::cout << "Could not create correct visual window.\n"; 383 | XCloseDisplay(display); 384 | return 1; 385 | } 386 | ``` 387 | When creating an OpenGL window the **XCreateSimpleWindow** function can no longer be used to create a window, instead the ```XCreateWindow(Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, int depth, unsigned int class, Visual *visual, unsigned long valuemask, XSetWindowAttributes *attributes);``` function must be used. The first argument is the display, the second argument is the parent of the window being created. Use the ```RootWindow(Display* display, int screen_number``` function to get this window. The x, y, width, height and border width paramaters are self explanatory. For the depth of the window use the depth field of the *XVisualInfo* class, the visual mask should be set to the *InputOutput* constant. Lastly a **XSetWindowAttributes** struct must be created. This struct describes all of the attributes the window will have. The following fields should be set: *border_pixel*, *background_pixel*, *override_redirect*, *colormap* and *event_mask*. The *colormap* field is very important, get it using the ```XCreateColormap(Display *display, Window w, Visual *visual, int alloc);``` function. The display and window are the same as always. The *Visual* object is contained within the *visual* field of the *XVisualInfo* object. Lastly the *alloc* argument should be set to the **AllocNone** constant. 388 | ``` 389 | XSetWindowAttributes windowAttribs; 390 | windowAttribs.border_pixel = BlackPixel(display, screenId); 391 | windowAttribs.background_pixel = WhitePixel(display, screenId); 392 | windowAttribs.override_redirect = True; 393 | windowAttribs.colormap = XCreateColormap(display, RootWindow(display, screenId), visual->visual, AllocNone); 394 | windowAttribs.event_mask = ExposureMask; 395 | window = XCreateWindow(display, RootWindow(display, screenId), 0, 0, 320, 200, 0, visual->depth, InputOutput, visual->visual, CWBackPixel | CWColormap | CWBorderPixel | CWEventMask, &windowAttribs); 396 | ``` 397 | After the window is created, an OpenGL context can be acquired with the ```glXCreateContext(Display* display, XVisualInfo* visual, GLXContext shareList, Bool direct); 398 | ``` function. The display and visual are the objects from before. The share list should be *NULL* and the direct should be set to true. Make the context active with the ```glXMakeCurrent(Display* display, Window window, GLXContext context)``` function. After a context is active the ```glGetString``` function can be used to retrieve information about the active OpenGL instance. 399 | ``` 400 | GLXContext context = glXCreateContext(display, visual, NULL, GL_TRUE); 401 | glXMakeCurrent(display, window, context); 402 | 403 | std::cout << "GL Vendor: " << glGetString(GL_VENDOR) << "\n"; 404 | std::cout << "GL Renderer: " << glGetString(GL_RENDERER) << "\n"; 405 | std::cout << "GL Version: " << glGetString(GL_VERSION) << "\n"; 406 | std::cout << "GL Shading Language: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << "\n"; 407 | ``` 408 | After a context is set active let the application enter it's update loop. Present the context of the active back buffer with the ```glXSwapBuffers(Display* display, Window window)``` function. 409 | ``` 410 | glClear(GL_COLOR_BUFFER_BIT); 411 | 412 | glBegin(GL_TRIANGLES); 413 | glColor3f( 1.0f, 0.0f, 0.0f); 414 | glVertex3f( 0.0f, -1.0f, 0.0f); 415 | glColor3f( 0.0f, 1.0f, 0.0f); 416 | glVertex3f(-1.0f, 1.0f, 0.0f); 417 | glColor3f( 0.0f, 0.0f, 1.0f); 418 | glVertex3f( 1.0f, 1.0f, 0.0f); 419 | glEnd(); 420 | 421 | // Present frame 422 | glXSwapBuffers(display, window); 423 | ``` 424 | Finally, once the update loop has finishes the context must be destroyed with the ```glXDestroyContext(Display* display, GLXContext context)``` function. The *Visual* needs to be cleaned up with **XFree** and the display and color map association should be destroyed with **XFreeColormap** 425 | ###Sample Code (C++) 426 | ``` 427 | #include 428 | #include 429 | #include 430 | #include 431 | #include 432 | #include 433 | 434 | int main(int argc, char** argv) { 435 | Display* display; 436 | Window window; 437 | Screen* screen; 438 | int screenId; 439 | XEvent ev; 440 | 441 | // Open the display 442 | display = XOpenDisplay(NULL); 443 | if (display == NULL) { 444 | std::cout << "Could not open display\n"; 445 | return 1; 446 | } 447 | screen = DefaultScreenOfDisplay(display); 448 | screenId = DefaultScreen(display); 449 | 450 | 451 | // Check GLX version 452 | GLint majorGLX, minorGLX = 0; 453 | glXQueryVersion(display, &majorGLX, &minorGLX); 454 | if (majorGLX <= 1 && minorGLX < 2) { 455 | std::cout << "GLX 1.2 or greater is required.\n"; 456 | XCloseDisplay(display); 457 | return 1; 458 | } 459 | else { 460 | std::cout << "GLX version: " << majorGLX << "." << minorGLX << '\n'; 461 | } 462 | 463 | // GLX, create XVisualInfo, this is the minimum visuals we want 464 | GLint glxAttribs[] = { 465 | GLX_RGBA, 466 | GLX_DOUBLEBUFFER, 467 | GLX_DEPTH_SIZE, 24, 468 | GLX_STENCIL_SIZE, 8, 469 | GLX_RED_SIZE, 8, 470 | GLX_GREEN_SIZE, 8, 471 | GLX_BLUE_SIZE, 8, 472 | GLX_SAMPLE_BUFFERS, 0, 473 | GLX_SAMPLES, 0, 474 | None 475 | }; 476 | XVisualInfo* visual = glXChooseVisual(display, screenId, glxAttribs); 477 | 478 | if (visual == 0) { 479 | std::cout << "Could not create correct visual window.\n"; 480 | XCloseDisplay(display); 481 | return 1; 482 | } 483 | 484 | // Open the window 485 | XSetWindowAttributes windowAttribs; 486 | windowAttribs.border_pixel = BlackPixel(display, screenId); 487 | windowAttribs.background_pixel = WhitePixel(display, screenId); 488 | windowAttribs.override_redirect = True; 489 | windowAttribs.colormap = XCreateColormap(display, RootWindow(display, screenId), visual->visual, AllocNone); 490 | windowAttribs.event_mask = ExposureMask; 491 | window = XCreateWindow(display, RootWindow(display, screenId), 0, 0, 320, 200, 0, visual->depth, InputOutput, visual->visual, CWBackPixel | CWColormap | CWBorderPixel | CWEventMask, &windowAttribs); 492 | 493 | // Create GLX OpenGL context 494 | GLXContext context = glXCreateContext(display, visual, NULL, GL_TRUE); 495 | glXMakeCurrent(display, window, context); 496 | 497 | std::cout << "GL Vendor: " << glGetString(GL_VENDOR) << "\n"; 498 | std::cout << "GL Renderer: " << glGetString(GL_RENDERER) << "\n"; 499 | std::cout << "GL Version: " << glGetString(GL_VERSION) << "\n"; 500 | std::cout << "GL Shading Language: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << "\n"; 501 | 502 | // Show the window 503 | XClearWindow(display, window); 504 | XMapRaised(display, window); 505 | 506 | // Set GL Sample stuff 507 | glClearColor(0.5f, 0.6f, 0.7f, 1.0f); 508 | 509 | // Enter message loop 510 | while (true) { 511 | XNextEvent(display, &ev); 512 | if (ev.type == Expose) { 513 | XWindowAttributes attribs; 514 | XGetWindowAttributes(display, window, &attribs); 515 | glViewport(0, 0, attribs.width, attribs.height); 516 | } 517 | 518 | // OpenGL Rendering 519 | glClear(GL_COLOR_BUFFER_BIT); 520 | 521 | glBegin(GL_TRIANGLES); 522 | glColor3f( 1.0f, 0.0f, 0.0f); 523 | glVertex3f( 0.0f, -1.0f, 0.0f); 524 | glColor3f( 0.0f, 1.0f, 0.0f); 525 | glVertex3f(-1.0f, 1.0f, 0.0f); 526 | glColor3f( 0.0f, 0.0f, 1.0f); 527 | glVertex3f( 1.0f, 1.0f, 0.0f); 528 | glEnd(); 529 | 530 | // Present frame 531 | glXSwapBuffers(display, window); 532 | } 533 | 534 | // Cleanup GLX 535 | glXDestroyContext(display, context); 536 | 537 | // Cleanup X11 538 | XFree(visual); 539 | XFreeColormap(display, windowAttribs.colormap); 540 | XDestroyWindow(display, window); 541 | XCloseDisplay(display); 542 | return 0; 543 | } 544 | ``` 545 | 546 | ##Getting a modern context 547 | Finding information on how to get a modern OpenGL context on X11 was rather difficult. Much of this part is adapted from https://www.opengl.org/wiki/Tutorial:_OpenGL_3.0_Context_Creation_(GLX). First, lets define a type for the ```glXCreateContextAttribsARB``` function and create a function that checks for supported extensions, this function will take two arguments, the extension list and target extension that is being queried. 548 | ``` 549 | typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); 550 | 551 | static bool isExtensionSupported(const char *extList, const char *extension) { 552 | const char *start; 553 | const char *where, *terminator; 554 | 555 | /* Extension names should not have spaces. */ 556 | where = strchr(extension, ' '); 557 | if (where || *extension == '\0') 558 | return false; 559 | 560 | /* It takes a bit of care to be fool-proof about parsing the 561 | OpenGL extensions string. Don't be fooled by sub-strings, 562 | etc. */ 563 | for (start=extList;;) { 564 | where = strstr(start, extension); 565 | 566 | if (!where) { 567 | break; 568 | } 569 | 570 | terminator = where + strlen(extension); 571 | 572 | if ( where == start || *(where - 1) == ' ' ) { 573 | if ( *terminator == ' ' || *terminator == '\0' ) { 574 | return true; 575 | } 576 | } 577 | 578 | start = terminator; 579 | } 580 | 581 | return false; 582 | } 583 | ``` 584 | The *glxAttribs* variable is going to be different for a modern context. 585 | ``` 586 | GLint glxAttribs[] = { 587 | GLX_X_RENDERABLE , True, 588 | GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, 589 | GLX_RENDER_TYPE , GLX_RGBA_BIT, 590 | GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR, 591 | GLX_RED_SIZE , 8, 592 | GLX_GREEN_SIZE , 8, 593 | GLX_BLUE_SIZE , 8, 594 | GLX_ALPHA_SIZE , 8, 595 | GLX_DEPTH_SIZE , 24, 596 | GLX_STENCIL_SIZE , 8, 597 | GLX_DOUBLEBUFFER , True, 598 | None 599 | ; 600 | ``` 601 | Once the desired attributes are defined, a compatible frame buffer needs to be created. This is done with the ```glXChooseFBConfig(Display* display, int screen, GLint* attribs, int* outCount``` funciton. This function will return an array of framebuffer configuration objects, as good as or better than what was specified in the *attribs* field. After the array is acquired it's ok to use the first one in the array. 602 | ``` 603 | int fbcount; 604 | GLXFBConfig* fbc = glXChooseFBConfig(display, DefaultScreen(display), glxAttribs, &fbcount); 605 | if (fbc == 0) { 606 | std::cout << "Failed to retrieve framebuffer.\n"; 607 | XCloseDisplay(display); 608 | return 1; 609 | } 610 | std::cout << "Found " << fbcount << " matching framebuffers.\n"; 611 | ``` 612 | **glXChooseVisual** can no longer be used to acquire a *XVisualInfo* object, instead ```glXGetVisualFromFBConfig(Display* display, GLXFBConfig fbConfig)``` must be used to take the best framebuffer into account. 613 | ``` 614 | XVisualInfo* visual = glXGetVisualFromFBConfig( display, bestFbc ); 615 | if (visual == 0) { 616 | std::cout << "Could not create correct visual window.\n"; 617 | XCloseDisplay(display); 618 | return 1; 619 | } 620 | ``` 621 | A pointer to the **glXCreateContextAttribsARB** function needs to be acquired trough the ```glXGetProcAddressARB(const GLubyte * strFuncName)``` function. 622 | ``` 623 | glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; 624 | glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" ); 625 | ``` 626 | To actually create the new context use the ```glXCreateNewContext``` function, this function is only available if the **GLX_ARB_create_context** extension is present. If the *glXCreateNewContext* function is not avilable fall back on the ```glXCreateContextAttribsARB``` function, if that is not available either the legacy ```glXCreateContext``` is always an option. 627 | ``` 628 | int context_attribs[] = { 629 | GLX_CONTEXT_MAJOR_VERSION_ARB, 3, 630 | GLX_CONTEXT_MINOR_VERSION_ARB, 2, 631 | GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, 632 | None 633 | }; 634 | 635 | const char *glxExts = glXQueryExtensionsString( display, screenId ); 636 | GLXContext context = 0; 637 | if (!isExtensionSupported( glxExts, "GLX_ARB_create_context")) { 638 | context = glXCreateNewContext( display, bestFbc, GLX_RGBA_TYPE, 0, True ); 639 | } 640 | else { 641 | context = glXCreateContextAttribsARB( display, bestFbc, 0, true, context_attribs ); 642 | } 643 | XSync( display, False ); 644 | ``` 645 | It wouldn't make much sense to run a non direct OpenGL context for games, glx provides the ```glXIsDirect(Display* display, GLXContext context)``` function to check for this. 646 | ``` 647 | // Verifying that context is a direct context 648 | if (!glXIsDirect (display, context)) { 649 | std::cout << "Indirect GLX rendering context obtained\n"; 650 | } 651 | else { 652 | std::cout << "Direct GLX rendering context obtained\n"; 653 | } 654 | ``` 655 | ###Sample Code (C++) 656 | ``` 657 | #include 658 | #include 659 | 660 | #include 661 | #include 662 | #include 663 | 664 | #include 665 | #include 666 | 667 | typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); 668 | 669 | static bool isExtensionSupported(const char *extList, const char *extension) { 670 | const char *start; 671 | const char *where, *terminator; 672 | 673 | where = strchr(extension, ' '); 674 | if (where || *extension == '\0') { 675 | return false; 676 | } 677 | 678 | for (start=extList;;) { 679 | where = strstr(start, extension); 680 | 681 | if (!where) { 682 | break; 683 | } 684 | 685 | terminator = where + strlen(extension); 686 | 687 | if ( where == start || *(where - 1) == ' ' ) { 688 | if ( *terminator == ' ' || *terminator == '\0' ) { 689 | return true; 690 | } 691 | } 692 | 693 | start = terminator; 694 | } 695 | 696 | return false; 697 | } 698 | 699 | int main(int argc, char** argv) { 700 | Display* display; 701 | Window window; 702 | Screen* screen; 703 | int screenId; 704 | XEvent ev; 705 | 706 | // Open the display 707 | display = XOpenDisplay(NULL); 708 | if (display == NULL) { 709 | std::cout << "Could not open display\n"; 710 | return 1; 711 | } 712 | screen = DefaultScreenOfDisplay(display); 713 | screenId = DefaultScreen(display); 714 | 715 | 716 | // Check GLX version 717 | GLint majorGLX, minorGLX = 0; 718 | glXQueryVersion(display, &majorGLX, &minorGLX); 719 | if (majorGLX <= 1 && minorGLX < 2) { 720 | std::cout << "GLX 1.2 or greater is required.\n"; 721 | XCloseDisplay(display); 722 | return 1; 723 | } 724 | else { 725 | std::cout << "GLX client version: " << glXGetClientString(display, GLX_VERSION) << '\n'; 726 | std::cout << "GLX client vendor: " << glXGetClientString(display, GLX_VENDOR) << "\n"; 727 | std::cout << "GLX client extensions:\n\t" << glXGetClientString(display, GLX_EXTENSIONS) << "\n"; 728 | 729 | std::cout << "GLX server version: " << glXQueryServerString(display, screenId, GLX_VERSION) << "\n"; 730 | std::cout << "GLX server vendoe: " << glXQueryServerString(display, screenId, GLX_VENDOR) << "\n"; 731 | std::cout << "GLX server extensions:\n\t " << glXQueryServerString(display, screenId, GLX_EXTENSIONS) << "\n"; 732 | } 733 | 734 | GLint glxAttribs[] = { 735 | GLX_X_RENDERABLE , True, 736 | GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, 737 | GLX_RENDER_TYPE , GLX_RGBA_BIT, 738 | GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR, 739 | GLX_RED_SIZE , 8, 740 | GLX_GREEN_SIZE , 8, 741 | GLX_BLUE_SIZE , 8, 742 | GLX_ALPHA_SIZE , 8, 743 | GLX_DEPTH_SIZE , 24, 744 | GLX_STENCIL_SIZE , 8, 745 | GLX_DOUBLEBUFFER , True, 746 | None 747 | }; 748 | 749 | int fbcount; 750 | GLXFBConfig* fbc = glXChooseFBConfig(display, screenId, glxAttribs, &fbcount); 751 | if (fbc == 0) { 752 | std::cout << "Failed to retrieve framebuffer.\n"; 753 | XCloseDisplay(display); 754 | return 1; 755 | } 756 | std::cout << "Found " << fbcount << " matching framebuffers.\n"; 757 | 758 | // Pick the FB config/visual with the most samples per pixel 759 | std::cout << "Getting best XVisualInfo\n"; 760 | int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999; 761 | for (int i = 0; i < fbcount; ++i) { 762 | XVisualInfo *vi = glXGetVisualFromFBConfig( display, fbc[i] ); 763 | if ( vi != 0) { 764 | int samp_buf, samples; 765 | glXGetFBConfigAttrib( display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf ); 766 | glXGetFBConfigAttrib( display, fbc[i], GLX_SAMPLES , &samples ); 767 | //std::cout << " Matching fbconfig " << i << ", SAMPLE_BUFFERS = " << samp_buf << ", SAMPLES = " << samples << ".\n"; 768 | 769 | if ( best_fbc < 0 || (samp_buf && samples > best_num_samp) ) { 770 | best_fbc = i; 771 | best_num_samp = samples; 772 | } 773 | if ( worst_fbc < 0 || !samp_buf || samples < worst_num_samp ) 774 | worst_fbc = i; 775 | worst_num_samp = samples; 776 | } 777 | XFree( vi ); 778 | } 779 | std::cout << "Best visual info index: " << best_fbc << "\n"; 780 | GLXFBConfig bestFbc = fbc[ best_fbc ]; 781 | XFree( fbc ); // Make sure to free this! 782 | 783 | XVisualInfo* visual = glXGetVisualFromFBConfig( display, bestFbc ); 784 | 785 | if (visual == 0) { 786 | std::cout << "Could not create correct visual window.\n"; 787 | XCloseDisplay(display); 788 | return 1; 789 | } 790 | 791 | if (screenId != visual->screen) { 792 | std::cout << "screenId(" << screenId << ") does not match visual->screen(" << visual->screen << ").\n"; 793 | XCloseDisplay(display); 794 | return 1; 795 | 796 | } 797 | 798 | // Open the window 799 | XSetWindowAttributes windowAttribs; 800 | windowAttribs.border_pixel = BlackPixel(display, screenId); 801 | windowAttribs.background_pixel = WhitePixel(display, screenId); 802 | windowAttribs.override_redirect = True; 803 | windowAttribs.colormap = XCreateColormap(display, RootWindow(display, screenId), visual->visual, AllocNone); 804 | windowAttribs.event_mask = ExposureMask; 805 | window = XCreateWindow(display, RootWindow(display, screenId), 0, 0, 320, 200, 0, visual->depth, InputOutput, visual->visual, CWBackPixel | CWColormap | CWBorderPixel | CWEventMask, &windowAttribs); 806 | 807 | // Create GLX OpenGL context 808 | glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; 809 | glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" ); 810 | 811 | const char *glxExts = glXQueryExtensionsString( display, screenId ); 812 | std::cout << "Late extensions:\n\t" << glxExts << "\n\n"; 813 | if (glXCreateContextAttribsARB == 0) { 814 | std::cout << "glXCreateContextAttribsARB() not found.\n"; 815 | } 816 | 817 | int context_attribs[] = { 818 | GLX_CONTEXT_MAJOR_VERSION_ARB, 3, 819 | GLX_CONTEXT_MINOR_VERSION_ARB, 2, 820 | GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, 821 | None 822 | }; 823 | 824 | GLXContext context = 0; 825 | if (!isExtensionSupported( glxExts, "GLX_ARB_create_context")) { 826 | context = glXCreateNewContext( display, bestFbc, GLX_RGBA_TYPE, 0, True ); 827 | } 828 | else { 829 | context = glXCreateContextAttribsARB( display, bestFbc, 0, true, context_attribs ); 830 | } 831 | XSync( display, False ); 832 | 833 | // Verifying that context is a direct context 834 | if (!glXIsDirect (display, context)) { 835 | std::cout << "Indirect GLX rendering context obtained\n"; 836 | } 837 | else { 838 | std::cout << "Direct GLX rendering context obtained\n"; 839 | } 840 | glXMakeCurrent(display, window, context); 841 | 842 | std::cout << "GL Vendor: " << glGetString(GL_VENDOR) << "\n"; 843 | std::cout << "GL Renderer: " << glGetString(GL_RENDERER) << "\n"; 844 | std::cout << "GL Version: " << glGetString(GL_VERSION) << "\n"; 845 | std::cout << "GL Shading Language: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << "\n"; 846 | 847 | // Show the window 848 | XClearWindow(display, window); 849 | XMapRaised(display, window); 850 | 851 | // Set GL Sample stuff 852 | glClearColor(0.5f, 0.6f, 0.7f, 1.0f); 853 | 854 | // Enter message loop 855 | while (true) { 856 | XNextEvent(display, &ev); 857 | if (ev.type == Expose) { 858 | XWindowAttributes attribs; 859 | XGetWindowAttributes(display, window, &attribs); 860 | glViewport(0, 0, attribs.width, attribs.height); 861 | } 862 | 863 | // OpenGL Rendering 864 | glClear(GL_COLOR_BUFFER_BIT); 865 | 866 | glBegin(GL_TRIANGLES); 867 | glColor3f( 1.0f, 0.0f, 0.0f); 868 | glVertex3f( 0.0f, -1.0f, 0.0f); 869 | glColor3f( 0.0f, 1.0f, 0.0f); 870 | glVertex3f(-1.0f, 1.0f, 0.0f); 871 | glColor3f( 0.0f, 0.0f, 1.0f); 872 | glVertex3f( 1.0f, 1.0f, 0.0f); 873 | glEnd(); 874 | 875 | // Present frame 876 | glXSwapBuffers(display, window); 877 | } 878 | 879 | // Cleanup GLX 880 | glXDestroyContext(display, context); 881 | 882 | // Cleanup X11 883 | XFree(visual); 884 | XFreeColormap(display, windowAttribs.colormap); 885 | XDestroyWindow(display, window); 886 | XCloseDisplay(display); 887 | return 0; 888 | } 889 | ``` 890 | #Part 4, Putting it all together 891 | Before putting all the pieces together, there is one more problem with the current implementation of the window. In order to close the window properly the **DestroyNotify** message needs to be handled. If *DestroyNotify* is recieved, the main loop should stop. To make the close button work, the **DeleteWindow** message must be treated as a client message; to remap it place the following code after the window was created, but before it is raised: 892 | ``` 893 | Atom atomWmDeleteWindow = XInternAtom(display, "WM_DELETE_WINDOW", False); 894 | XSetWMProtocols(display, window, &atomWmDeleteWindow, 1); 895 | ``` 896 | After the *DeleteWindow* message has been remapped as a client message subscribe to **StructureNotifyMask**. Finally, make sure to handle the new cases in the message loop. 897 | ``` 898 | XNextEvent(display, &ev); 899 | if (ev.type == ClientMessage) { 900 | if (ev.xclient.data.l[0] == atomWmDeleteWindow) { 901 | break; 902 | } 903 | } 904 | else if (ev.type == DestroyNotify) { 905 | break; 906 | } 907 | ``` 908 | ###Sample Code (C++) 909 | ``` 910 | #include 911 | #include 912 | 913 | #include 914 | #include 915 | #include 916 | 917 | #include 918 | #include 919 | 920 | #include 921 | #include 922 | 923 | #define WINDOW_WIDTH 800 924 | #define WINDOW_HEIGHT 600 925 | #define FPS 30 926 | #define TEST_LOCAL 927 | 928 | extern bool Initialize(int w, int h); 929 | extern bool Update(float deltaTime); 930 | extern void Render(); 931 | extern void Resize(int w, int h); 932 | extern void Shutdown(); 933 | 934 | typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); 935 | 936 | #define SKIP_TICKS (1000 / FPS) 937 | 938 | static double GetMilliseconds() { 939 | static timeval s_tTimeVal; 940 | gettimeofday(&s_tTimeVal, NULL); 941 | double time = s_tTimeVal.tv_sec * 1000.0; // sec to ms 942 | time += s_tTimeVal.tv_usec / 1000.0; // us to ms 943 | return time; 944 | } 945 | 946 | static bool isExtensionSupported(const char *extList, const char *extension) { 947 | return strstr(extList, extension) != 0; 948 | } 949 | 950 | int main(int argc, char** argv) { 951 | Display* display; 952 | Window window; 953 | Screen* screen; 954 | int screenId; 955 | XEvent ev; 956 | 957 | // Open the display 958 | display = XOpenDisplay(NULL); 959 | if (display == NULL) { 960 | std::cout << "Could not open display\n"; 961 | return 1; 962 | } 963 | screen = DefaultScreenOfDisplay(display); 964 | screenId = DefaultScreen(display); 965 | 966 | // Check GLX version 967 | GLint majorGLX, minorGLX = 0; 968 | glXQueryVersion(display, &majorGLX, &minorGLX); 969 | if (majorGLX <= 1 && minorGLX < 2) { 970 | std::cout << "GLX 1.2 or greater is required.\n"; 971 | XCloseDisplay(display); 972 | return 1; 973 | } 974 | 975 | GLint glxAttribs[] = { 976 | GLX_X_RENDERABLE , True, 977 | GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, 978 | GLX_RENDER_TYPE , GLX_RGBA_BIT, 979 | GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR, 980 | GLX_RED_SIZE , 8, 981 | GLX_GREEN_SIZE , 8, 982 | GLX_BLUE_SIZE , 8, 983 | GLX_ALPHA_SIZE , 8, 984 | GLX_DEPTH_SIZE , 24, 985 | GLX_STENCIL_SIZE , 8, 986 | GLX_DOUBLEBUFFER , True, 987 | None 988 | }; 989 | 990 | int fbcount; 991 | GLXFBConfig* fbc = glXChooseFBConfig(display, screenId, glxAttribs, &fbcount); 992 | if (fbc == 0) { 993 | std::cout << "Failed to retrieve framebuffer.\n"; 994 | XCloseDisplay(display); 995 | return 1; 996 | } 997 | 998 | // Pick the FB config/visual with the most samples per pixel 999 | int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999; 1000 | for (int i = 0; i < fbcount; ++i) { 1001 | XVisualInfo *vi = glXGetVisualFromFBConfig( display, fbc[i] ); 1002 | if ( vi != 0) { 1003 | int samp_buf, samples; 1004 | glXGetFBConfigAttrib( display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf ); 1005 | glXGetFBConfigAttrib( display, fbc[i], GLX_SAMPLES , &samples ); 1006 | 1007 | if ( best_fbc < 0 || (samp_buf && samples > best_num_samp) ) { 1008 | best_fbc = i; 1009 | best_num_samp = samples; 1010 | } 1011 | if ( worst_fbc < 0 || !samp_buf || samples < worst_num_samp ) 1012 | worst_fbc = i; 1013 | worst_num_samp = samples; 1014 | } 1015 | XFree( vi ); 1016 | } 1017 | GLXFBConfig bestFbc = fbc[ best_fbc ]; 1018 | XFree( fbc ); // Make sure to free this! 1019 | 1020 | 1021 | XVisualInfo* visual = glXGetVisualFromFBConfig( display, bestFbc ); 1022 | if (visual == 0) { 1023 | std::cout << "Could not create correct visual window.\n"; 1024 | XCloseDisplay(display); 1025 | return 1; 1026 | } 1027 | 1028 | if (screenId != visual->screen) { 1029 | std::cout << "screenId(" << screenId << ") does not match visual->screen(" << visual->screen << ").\n"; 1030 | XCloseDisplay(display); 1031 | return 1; 1032 | 1033 | } 1034 | 1035 | // Open the window 1036 | XSetWindowAttributes windowAttribs; 1037 | windowAttribs.border_pixel = BlackPixel(display, screenId); 1038 | windowAttribs.background_pixel = WhitePixel(display, screenId); 1039 | windowAttribs.override_redirect = True; 1040 | windowAttribs.colormap = XCreateColormap(display, RootWindow(display, screenId), visual->visual, AllocNone); 1041 | windowAttribs.event_mask = ExposureMask; 1042 | window = XCreateWindow(display, RootWindow(display, screenId), 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, visual->depth, InputOutput, visual->visual, CWBackPixel | CWColormap | CWBorderPixel | CWEventMask, &windowAttribs); 1043 | 1044 | // Redirect Close 1045 | Atom atomWmDeleteWindow = XInternAtom(display, "WM_DELETE_WINDOW", False); 1046 | XSetWMProtocols(display, window, &atomWmDeleteWindow, 1); 1047 | 1048 | // Create GLX OpenGL context 1049 | glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; 1050 | glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" ); 1051 | 1052 | int context_attribs[] = { 1053 | GLX_CONTEXT_MAJOR_VERSION_ARB, 3, 1054 | GLX_CONTEXT_MINOR_VERSION_ARB, 2, 1055 | GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, 1056 | None 1057 | }; 1058 | 1059 | GLXContext context = 0; 1060 | const char *glxExts = glXQueryExtensionsString( display, screenId ); 1061 | if (!isExtensionSupported( glxExts, "GLX_ARB_create_context")) { 1062 | std::cout << "GLX_ARB_create_context not supported\n"; 1063 | context = glXCreateNewContext( display, bestFbc, GLX_RGBA_TYPE, 0, True ); 1064 | } 1065 | else { 1066 | context = glXCreateContextAttribsARB( display, bestFbc, 0, true, context_attribs ); 1067 | } 1068 | XSync( display, False ); 1069 | 1070 | // Verifying that context is a direct context 1071 | if (!glXIsDirect (display, context)) { 1072 | std::cout << "Indirect GLX rendering context obtained\n"; 1073 | } 1074 | else { 1075 | std::cout << "Direct GLX rendering context obtained\n"; 1076 | } 1077 | glXMakeCurrent(display, window, context); 1078 | 1079 | std::cout << "GL Renderer: " << glGetString(GL_RENDERER) << "\n"; 1080 | std::cout << "GL Version: " << glGetString(GL_VERSION) << "\n"; 1081 | std::cout << "GLSL Version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << "\n"; 1082 | 1083 | if (!Initialize(WINDOW_WIDTH, WINDOW_HEIGHT)) { 1084 | glXDestroyContext(display, context); 1085 | XFree(visual); 1086 | XFreeColormap(display, windowAttribs.colormap); 1087 | XDestroyWindow(display, window); 1088 | XCloseDisplay(display); 1089 | return 1; 1090 | } 1091 | 1092 | // Show the window 1093 | XClearWindow(display, window); 1094 | XMapRaised(display, window); 1095 | 1096 | double prevTime = GetMilliseconds(); 1097 | double currentTime = GetMilliseconds(); 1098 | double deltaTime = 0.0; 1099 | 1100 | timeval time; 1101 | long sleepTime = 0; 1102 | gettimeofday(&time, NULL); 1103 | long nextGameTick = (time.tv_sec * 1000) + (time.tv_usec / 1000); 1104 | 1105 | // Enter message loop 1106 | while (true) { 1107 | if (XPending(display) > 0) { 1108 | XNextEvent(display, &ev); 1109 | if (ev.type == Expose) { 1110 | XWindowAttributes attribs; 1111 | XGetWindowAttributes(display, window, &attribs); 1112 | Resize(attribs.width, attribs.height); 1113 | } 1114 | if (ev.type == ClientMessage) { 1115 | if (ev.xclient.data.l[0] == atomWmDeleteWindow) { 1116 | break; 1117 | } 1118 | } 1119 | else if (ev.type == DestroyNotify) { 1120 | break; 1121 | } 1122 | } 1123 | 1124 | currentTime = GetMilliseconds(); 1125 | deltaTime = double(currentTime - prevTime) * 0.001; 1126 | prevTime = currentTime; 1127 | 1128 | if (!Update((float)deltaTime)) { 1129 | break; 1130 | } 1131 | Render(); 1132 | 1133 | // Present frame 1134 | glXSwapBuffers(display, window); 1135 | 1136 | // Limit Framerate 1137 | gettimeofday(&time, NULL); 1138 | nextGameTick += SKIP_TICKS; 1139 | sleepTime = nextGameTick - ((time.tv_sec * 1000) + (time.tv_usec / 1000)); 1140 | usleep((unsigned int)(sleepTime / 1000)); 1141 | } 1142 | 1143 | std::cout << "Shutting Down\n"; 1144 | Shutdown(); 1145 | 1146 | // Cleanup GLX 1147 | glXDestroyContext(display, context); 1148 | 1149 | // Cleanup X11 1150 | XFree(visual); 1151 | XFreeColormap(display, windowAttribs.colormap); 1152 | XDestroyWindow(display, window); 1153 | XCloseDisplay(display); 1154 | return 0; 1155 | } 1156 | 1157 | #ifdef TEST_LOCAL 1158 | bool Initialize(int w, int h) { 1159 | glClearColor(0.5f, 0.6f, 0.7f, 1.0f); 1160 | glViewport(0, 0, w, h); 1161 | return true; 1162 | } 1163 | 1164 | bool Update(float deltaTime) { 1165 | return true; 1166 | } 1167 | 1168 | void Render() { 1169 | glClear(GL_COLOR_BUFFER_BIT); 1170 | 1171 | glBegin(GL_TRIANGLES); 1172 | glColor3f( 1.0f, 0.0f, 0.0f); 1173 | glVertex3f( 0.0f, -1.0f, 0.0f); 1174 | glColor3f( 0.0f, 1.0f, 0.0f); 1175 | glVertex3f(-1.0f, 1.0f, 0.0f); 1176 | glColor3f( 0.0f, 0.0f, 1.0f); 1177 | glVertex3f( 1.0f, 1.0f, 0.0f); 1178 | glEnd(); 1179 | } 1180 | 1181 | void Resize(int w, int h) { 1182 | glViewport(0, 0, w, h); 1183 | } 1184 | 1185 | void Shutdown() { 1186 | 1187 | } 1188 | #endif 1189 | ``` 1190 | 1191 | #Sources 1192 | * http://xopendisplay.hilltopia.ca/2009/Jan/Xlib-tutorial-part-1----Beginnings.html 1193 | * http://tronche.com/gui/x/xlib/display/opening.html 1194 | * http://techpubs.sgi.com/library/dynaweb_docs/0640/SGI_Developer/books/OpenGL_Porting/sgi_html/ch04.html 1195 | * http://www.unix-manuals.com/tutorials/xlib/xlib.html 1196 | * http://nehe.gamedev.net/tutorial/simpleglxwindow_class/35003/ 1197 | * http://tronche.com/gui/x/xlib/events/keyboard-pointer/keyboard-pointer.html 1198 | * https://github.com/blackberry/GamePlay/blob/master/gameplay%2Fsrc%2FPlatformLinux.cpp 1199 | * http://www-h.eng.cam.ac.uk/help/tpl/graphics/X/X11R5/node25.html 1200 | * http://tronche.com/gui/x/xlib/input/pointer-grabbing.html 1201 | * http://tronche.com/gui/x/xlib/ICC/client-to-window-manager/XStoreName.html 1202 | * http://tronche.com/gui/x/xlib/window/configure.html#XWindowChanges 1203 | * http://tronche.com/gui/x/xlib/window/XConfigureWindow.html 1204 | * http://www.x.org/archive/X11R7.5/doc/man/man3/XVisualInfo.3.html 1205 | * http://msdn.microsoft.com/en-us/library/windows/desktop/dd318252(v=vs.85).aspx 1206 | * https://gist.github.com/kovrov/1304027 1207 | * https://www.youtube.com/watch?v=qWiqRIoKChg 1208 | * https://www.opengl.org/wiki/Tutorial:_OpenGL_3.0_Context_Creation_(GLX) -------------------------------------------------------------------------------- /Sources.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gamedevtech/X11OpenGLWindow/4a3d55bb7aafd135670947f71bd2a3ee691d3fb3/Sources.zip -------------------------------------------------------------------------------- /X11.cpp: -------------------------------------------------------------------------------- 1 | // g++ -o X11Window X11.cpp -lX11 -lGL -lGLEW -L/usr/X11/lib -I/opt/X11/include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #define WINDOW_WIDTH 800 16 | #define WINDOW_HEIGHT 600 17 | #define FPS 30 18 | #define TEST_LOCAL 19 | 20 | extern bool Initialize(int w, int h); 21 | extern bool Update(float deltaTime); 22 | extern void Render(); 23 | extern void Resize(int w, int h); 24 | extern void Shutdown(); 25 | 26 | typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); 27 | 28 | #define SKIP_TICKS (1000 / FPS) 29 | 30 | static double GetMilliseconds() { 31 | static timeval s_tTimeVal; 32 | gettimeofday(&s_tTimeVal, NULL); 33 | double time = s_tTimeVal.tv_sec * 1000.0; // sec to ms 34 | time += s_tTimeVal.tv_usec / 1000.0; // us to ms 35 | return time; 36 | } 37 | 38 | static bool isExtensionSupported(const char *extList, const char *extension) { 39 | return strstr(extList, extension) != 0; 40 | } 41 | 42 | int main(int argc, char** argv) { 43 | Display* display; 44 | Window window; 45 | Screen* screen; 46 | int screenId; 47 | XEvent ev; 48 | 49 | // Open the display 50 | display = XOpenDisplay(NULL); 51 | if (display == NULL) { 52 | std::cout << "Could not open display\n"; 53 | return 1; 54 | } 55 | screen = DefaultScreenOfDisplay(display); 56 | screenId = DefaultScreen(display); 57 | 58 | // Check GLX version 59 | GLint majorGLX, minorGLX = 0; 60 | glXQueryVersion(display, &majorGLX, &minorGLX); 61 | if (majorGLX <= 1 && minorGLX < 2) { 62 | std::cout << "GLX 1.2 or greater is required.\n"; 63 | XCloseDisplay(display); 64 | return 1; 65 | } 66 | 67 | GLint glxAttribs[] = { 68 | GLX_X_RENDERABLE , True, 69 | GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, 70 | GLX_RENDER_TYPE , GLX_RGBA_BIT, 71 | GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR, 72 | GLX_RED_SIZE , 8, 73 | GLX_GREEN_SIZE , 8, 74 | GLX_BLUE_SIZE , 8, 75 | GLX_ALPHA_SIZE , 8, 76 | GLX_DEPTH_SIZE , 24, 77 | GLX_STENCIL_SIZE , 8, 78 | GLX_DOUBLEBUFFER , True, 79 | None 80 | }; 81 | 82 | int fbcount; 83 | GLXFBConfig* fbc = glXChooseFBConfig(display, screenId, glxAttribs, &fbcount); 84 | if (fbc == 0) { 85 | std::cout << "Failed to retrieve framebuffer.\n"; 86 | XCloseDisplay(display); 87 | return 1; 88 | } 89 | 90 | // Pick the FB config/visual with the most samples per pixel 91 | int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999; 92 | for (int i = 0; i < fbcount; ++i) { 93 | XVisualInfo *vi = glXGetVisualFromFBConfig( display, fbc[i] ); 94 | if ( vi != 0) { 95 | int samp_buf, samples; 96 | glXGetFBConfigAttrib( display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf ); 97 | glXGetFBConfigAttrib( display, fbc[i], GLX_SAMPLES , &samples ); 98 | 99 | if ( best_fbc < 0 || (samp_buf && samples > best_num_samp) ) { 100 | best_fbc = i; 101 | best_num_samp = samples; 102 | } 103 | if ( worst_fbc < 0 || !samp_buf || samples < worst_num_samp ) 104 | worst_fbc = i; 105 | worst_num_samp = samples; 106 | } 107 | XFree( vi ); 108 | } 109 | GLXFBConfig bestFbc = fbc[ best_fbc ]; 110 | XFree( fbc ); // Make sure to free this! 111 | 112 | 113 | XVisualInfo* visual = glXGetVisualFromFBConfig( display, bestFbc ); 114 | if (visual == 0) { 115 | std::cout << "Could not create correct visual window.\n"; 116 | XCloseDisplay(display); 117 | return 1; 118 | } 119 | 120 | if (screenId != visual->screen) { 121 | std::cout << "screenId(" << screenId << ") does not match visual->screen(" << visual->screen << ").\n"; 122 | XCloseDisplay(display); 123 | return 1; 124 | 125 | } 126 | 127 | // Open the window 128 | XSetWindowAttributes windowAttribs; 129 | windowAttribs.border_pixel = BlackPixel(display, screenId); 130 | windowAttribs.background_pixel = WhitePixel(display, screenId); 131 | windowAttribs.override_redirect = True; 132 | windowAttribs.colormap = XCreateColormap(display, RootWindow(display, screenId), visual->visual, AllocNone); 133 | windowAttribs.event_mask = ExposureMask; 134 | window = XCreateWindow(display, RootWindow(display, screenId), 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, visual->depth, InputOutput, visual->visual, CWBackPixel | CWColormap | CWBorderPixel | CWEventMask, &windowAttribs); 135 | 136 | // Redirect Close 137 | Atom atomWmDeleteWindow = XInternAtom(display, "WM_DELETE_WINDOW", False); 138 | XSetWMProtocols(display, window, &atomWmDeleteWindow, 1); 139 | 140 | // Create GLX OpenGL context 141 | glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; 142 | glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" ); 143 | 144 | int context_attribs[] = { 145 | GLX_CONTEXT_MAJOR_VERSION_ARB, 3, 146 | GLX_CONTEXT_MINOR_VERSION_ARB, 2, 147 | GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, 148 | None 149 | }; 150 | 151 | GLXContext context = 0; 152 | const char *glxExts = glXQueryExtensionsString( display, screenId ); 153 | if (!isExtensionSupported( glxExts, "GLX_ARB_create_context")) { 154 | std::cout << "GLX_ARB_create_context not supported\n"; 155 | context = glXCreateNewContext( display, bestFbc, GLX_RGBA_TYPE, 0, True ); 156 | } 157 | else { 158 | context = glXCreateContextAttribsARB( display, bestFbc, 0, true, context_attribs ); 159 | } 160 | XSync( display, False ); 161 | 162 | // Verifying that context is a direct context 163 | if (!glXIsDirect (display, context)) { 164 | std::cout << "Indirect GLX rendering context obtained\n"; 165 | } 166 | else { 167 | std::cout << "Direct GLX rendering context obtained\n"; 168 | } 169 | glXMakeCurrent(display, window, context); 170 | 171 | std::cout << "GL Renderer: " << glGetString(GL_RENDERER) << "\n"; 172 | std::cout << "GL Version: " << glGetString(GL_VERSION) << "\n"; 173 | std::cout << "GLSL Version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << "\n"; 174 | 175 | if (!Initialize(WINDOW_WIDTH, WINDOW_HEIGHT)) { 176 | glXDestroyContext(display, context); 177 | XFree(visual); 178 | XFreeColormap(display, windowAttribs.colormap); 179 | XDestroyWindow(display, window); 180 | XCloseDisplay(display); 181 | return 1; 182 | } 183 | 184 | // Show the window 185 | XClearWindow(display, window); 186 | XMapRaised(display, window); 187 | 188 | double prevTime = GetMilliseconds(); 189 | double currentTime = GetMilliseconds(); 190 | double deltaTime = 0.0; 191 | 192 | timeval time; 193 | long sleepTime = 0; 194 | gettimeofday(&time, NULL); 195 | long nextGameTick = (time.tv_sec * 1000) + (time.tv_usec / 1000); 196 | 197 | // Enter message loop 198 | while (true) { 199 | if (XPending(display) > 0) { 200 | XNextEvent(display, &ev); 201 | if (ev.type == Expose) { 202 | XWindowAttributes attribs; 203 | XGetWindowAttributes(display, window, &attribs); 204 | Resize(attribs.width, attribs.height); 205 | } 206 | if (ev.type == ClientMessage) { 207 | if (ev.xclient.data.l[0] == atomWmDeleteWindow) { 208 | break; 209 | } 210 | } 211 | else if (ev.type == DestroyNotify) { 212 | break; 213 | } 214 | } 215 | 216 | currentTime = GetMilliseconds(); 217 | deltaTime = double(currentTime - prevTime) * 0.001; 218 | prevTime = currentTime; 219 | 220 | if (!Update((float)deltaTime)) { 221 | break; 222 | } 223 | Render(); 224 | 225 | // Present frame 226 | glXSwapBuffers(display, window); 227 | 228 | gettimeofday(&time, NULL); 229 | nextGameTick += SKIP_TICKS; 230 | sleepTime = nextGameTick - ((time.tv_sec * 1000) + (time.tv_usec / 1000)); 231 | usleep((unsigned int)(sleepTime / 1000)); 232 | } 233 | 234 | std::cout << "Shutting Down\n"; 235 | Shutdown(); 236 | 237 | // Cleanup GLX 238 | glXDestroyContext(display, context); 239 | 240 | // Cleanup X11 241 | XFree(visual); 242 | XFreeColormap(display, windowAttribs.colormap); 243 | XDestroyWindow(display, window); 244 | XCloseDisplay(display); 245 | return 0; 246 | } 247 | 248 | #ifdef TEST_LOCAL 249 | bool Initialize(int w, int h) { 250 | glClearColor(0.5f, 0.6f, 0.7f, 1.0f); 251 | glViewport(0, 0, w, h); 252 | return true; 253 | } 254 | 255 | bool Update(float deltaTime) { 256 | return true; 257 | } 258 | 259 | void Render() { 260 | glClear(GL_COLOR_BUFFER_BIT); 261 | 262 | glBegin(GL_TRIANGLES); 263 | glColor3f( 1.0f, 0.0f, 0.0f); 264 | glVertex3f( 0.0f, -1.0f, 0.0f); 265 | glColor3f( 0.0f, 1.0f, 0.0f); 266 | glVertex3f(-1.0f, 1.0f, 0.0f); 267 | glColor3f( 0.0f, 0.0f, 1.0f); 268 | glVertex3f( 1.0f, 1.0f, 0.0f); 269 | glEnd(); 270 | } 271 | 272 | void Resize(int w, int h) { 273 | glViewport(0, 0, w, h); 274 | } 275 | 276 | void Shutdown() { 277 | 278 | } 279 | #endif --------------------------------------------------------------------------------