From cd80702bb3cb4ffec187ae182a9ed84c46c869c3 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Mon, 1 Jul 2019 00:10:35 -0400 Subject: [PATCH] Initial implementation of xcb/xlib/glx version --- blit.c | 5 + blit3.c | 334 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ build.sh | 5 +- 3 files changed, 340 insertions(+), 4 deletions(-) create mode 100644 blit3.c diff --git a/blit.c b/blit.c index d189d04..45d8ba8 100644 --- a/blit.c +++ b/blit.c @@ -1,9 +1,14 @@ +#include +#include +#include /* for XGetXCBConnection, link with libX11-xcb */ +#include #include #include #include #include #include #include +#include xcb_alloc_color_reply_t* getColor(xcb_connection_t*, diff --git a/blit3.c b/blit3.c new file mode 100644 index 0000000..f862dce --- /dev/null +++ b/blit3.c @@ -0,0 +1,334 @@ +#include +#include +#include +#include /* for XGetXCBConnection, link with libX11-xcb */ +#include +#include +#include + +xcb_window_t +getWindow(xcb_connection_t*, + xcb_colormap_t, + int, + xcb_screen_t*); + +xcb_colormap_t +allocateColorMap(xcb_connection_t*, + xcb_screen_t*, + int); + +GLXContext +getGLXContext(Display*, + GLXFBConfig); + +/* + Attribs filter the list of FBConfigs returned by glXChooseFBConfig(). + Visual attribs further described in glXGetFBConfigAttrib(3) +*/ +static int visual_attribs[] = { + 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, + //GLX_SAMPLE_BUFFERS , 1, + //GLX_SAMPLES , 4, + None +}; + +/* Macro definition to parse X server events + * The ~0x80 is needed to get the lower 7 bits + */ +#define RECEIVE_EVENT(ev) (ev->response_type & ~0x80) + +void draw() { + glClearColor(0.2, 0.4, 0.9, 1.0); + glClear(GL_COLOR_BUFFER_BIT); +} + +int +main_loop(Display *display, + xcb_connection_t *xcb_display, + xcb_window_t window, + GLXDrawable drawable) { + + int running = 1; + + while (running) { + /* Wait for event */ + xcb_generic_event_t *event = xcb_wait_for_event(xcb_display); + if (!event) { + fprintf(stderr, "i/o error in xcb_wait_for_event"); + return -1; + } + + switch (RECEIVE_EVENT(event)) { + case XCB_KEY_PRESS: + /* Quit on key press */ + running = 0; + break; + case XCB_EXPOSE: + /* Handle expose event, draw and swap buffers */ + draw(); + + /* This is where the magic happens */ + /* This call will NOT block.*/ + /* It will be sync'd with vertical refresh */ + glXSwapBuffers(display, drawable); + break; + default: + break; + } + + free(event); + } + + return 0; +} + +int +setup_and_run(Display* display, + xcb_connection_t *xcb_display, + int default_screen, + xcb_screen_t *screen) { + + int visualID = 0; + + /* Query framebuffer configurations that match visual_attribs */ + GLXFBConfig *fb_configs = 0; + + int num_fb_configs = 0; + + fb_configs = glXChooseFBConfig(display, + default_screen, + visual_attribs, + &num_fb_configs); + + if (!fb_configs || num_fb_configs == 0) { + fprintf(stderr, "glXGetFBConfigs failed\n"); + return -1; + } + + printf("Found %d matching FB configs", num_fb_configs); + + /* Select first framebuffer config and query visualID */ + GLXFBConfig fb_config = fb_configs[0]; + glXGetFBConfigAttrib(display, fb_config, GLX_VISUAL_ID , &visualID); + + /* Create XID's for colormap and window */ + xcb_colormap_t colormap = allocateColorMap(xcb_display, screen, visualID); + + xcb_window_t window = getWindow(xcb_display, colormap, visualID, screen); + + // NOTE: window must be mapped before glXMakeContextCurrent + xcb_map_window(xcb_display, window); + + + /* Create GLX Window */ + GLXContext context = getGLXContext(display, fb_config); + + /* This is going to be our backbuffer */ + GLXDrawable drawable = 0; + + GLXWindow glxwindow = + glXCreateWindow( + display, + fb_config, + window, + 0 + ); + + if (!window) { + xcb_destroy_window(xcb_display, window); + glXDestroyContext(display, context); + + fprintf(stderr, "glXDestroyContext failed\n"); + return -1; + } + + drawable = glxwindow; + + /* make OpenGL context current */ + /* This will allow us to write to it with the GLX functions */ + if (!glXMakeContextCurrent(display, + drawable, + drawable, + context)) { + + xcb_destroy_window(xcb_display, window); + glXDestroyContext(display, context); + + fprintf(stderr, "glXMakeContextCurrent failed\n"); + return -1; + } + + /* run main loop */ + int retval = main_loop(display, xcb_display, window, drawable); + + /* Cleanup */ + glXDestroyWindow(display, glxwindow); + + xcb_destroy_window(xcb_display, window); + + glXDestroyContext(display, context); + + return retval; +} + + +GLXContext +getGLXContext(Display *display, + GLXFBConfig fb_config) { + /* Create GLX Window */ + GLXContext context; + + /* Create OpenGL context */ + context = glXCreateNewContext(display, + fb_config, + GLX_RGBA_TYPE, + 0, + True); + if (!context) { + fprintf(stderr, "glXCreateNewContext failed\n"); + exit(1); + } + + return context; + +} + + +xcb_colormap_t +allocateColorMap(xcb_connection_t *xcb_display, + xcb_screen_t *screen, + int visualID) { + /* Create XID's for colormap and window */ + xcb_colormap_t colormap = xcb_generate_id(xcb_display); + + /* Create colormap */ + xcb_create_colormap( + xcb_display, + XCB_COLORMAP_ALLOC_NONE, + colormap, + screen->root, + visualID + ); + + return colormap; + +} + + +xcb_window_t +getWindow(xcb_connection_t *xcb_display, + xcb_colormap_t colormap, + int visualID, + xcb_screen_t *screen) { + xcb_window_t window = xcb_generate_id(xcb_display); + + /* Create window */ + uint32_t eventmask = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS; + uint32_t valuelist[] = { eventmask, colormap, 0 }; + uint32_t valuemask = XCB_CW_EVENT_MASK | XCB_CW_COLORMAP; + + xcb_create_window( + xcb_display, + XCB_COPY_FROM_PARENT, + window, + screen->root, + 0, 0, + 150, 150, + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + visualID, + valuemask, + valuelist + ); + + return window; +} + +Display* +getDisplay() { + Display *display; + + if (display == NULL) { + fprintf(stderr, "Could not open the display! :(\n"); + exit(1); + } + + /* Open Xlib Display */ + return XOpenDisplay(0); +} + +xcb_connection_t* +getXCBDisplay(Display *x_display) { + /* Get the XCB connection from the display */ + /* See https://xcb.freedesktop.org/MixingCalls/ */ + + xcb_connection_t *xcb_display = XGetXCBConnection(x_display); + + if (!xcb_display) { + XCloseDisplay(x_display); + fprintf(stderr, "Can't get xcb connection from display\n"); + exit(1); + } + return xcb_display; +} + +xcb_screen_t* +getScreen(Display *display, + xcb_connection_t *xcb_display, + int default_screen) { + + /* Find XCB screen */ + xcb_screen_t *screen = 0; + + xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator( + xcb_get_setup(xcb_display) + ); + + int screen_num = default_screen; + printf("%d\n", screen_num); + + /* Handle multiple screens */ + /* We want screen number 0 */ + while(screen_iter.rem && screen_num > 0) { + printf("%d\n", screen_num); + + screen_num--; + xcb_screen_next(&screen_iter); + } + + return screen_iter.data; +} + +int main(int argc, char* argv[]) +{ + Display *display = getDisplay(); + + /* Get the XCB connection from the display */ + xcb_connection_t *xcb_display = getXCBDisplay(display); + + /* Acquire event queue ownership */ + /* See https://xcb.freedesktop.org/MixingCalls/ for why this is needed */ + XSetEventQueueOwner(display, XCBOwnsEventQueue); + + int default_screen = DefaultScreen(display); + + xcb_screen_t *screen = getScreen(display, xcb_display, default_screen); + + /* Initialize window and OpenGL context, run main loop and deinitialize */ + int retval = setup_and_run(display, xcb_display, default_screen, screen); + + /* Cleanup */ + XCloseDisplay(display); + + return retval; +} diff --git a/build.sh b/build.sh index a289da5..1da2a15 100755 --- a/build.sh +++ b/build.sh @@ -1,7 +1,4 @@ #! /usr/bin/env bash +gcc $(pkg-config --cflags --libs x11 x11-xcb xcb gl xcb-glx) blit3.c -args=$(pkg-config --cflags --libs xcb x11) - -#gcc $args blit.c -gcc $args blit2.c