├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── db_none.c ├── db_pixmap.c ├── db_xdbe.c ├── db_ximage.c └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | main 2 | main.none 3 | main.xdbe 4 | main.pixmap 5 | main.ximage -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Alexey Kutepov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-Wall -Wextra -std=c11 -pedantic -ggdb 2 | LIBS=-lX11 -lXext 3 | 4 | .PHONY: all 5 | all: main.none main.xdbe main.pixmap main.ximage 6 | 7 | main.none: main.c 8 | $(CC) $(CFLAGS) -DDB_IMPL=DB_NONE -o main.none main.c $(LIBS) 9 | 10 | main.xdbe: main.c 11 | $(CC) $(CFLAGS) -DDB_IMPL=DB_XDBE -o main.xdbe main.c $(LIBS) 12 | 13 | main.pixmap: main.c 14 | $(CC) $(CFLAGS) -DDB_IMPL=DB_PIXMAP -o main.pixmap main.c $(LIBS) 15 | 16 | main.ximage: main.c 17 | $(CC) $(CFLAGS) -DDB_IMPL=DB_XIMAGE -o main.ximage main.c $(LIBS) 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # X11 Double Buffering Experiment 2 | 3 | Trying different approaches to Double Buffering in Pure X11 applications on Linux: 4 | 5 | - [No Double Buffering](./db_none.c) 6 | - [Xdbe](./db_xdbe.c) 7 | - [Back Buffer in Pixmap](./db_pixmap.c) 8 | - [Back Buffer in XImage](./db_ximage.c) 9 | 10 | ## Quick Start 11 | 12 | ```console 13 | $ make 14 | $ ./main.none 15 | $ ./main.xdbe 16 | $ ./main.pixmap 17 | $ ./main.ximage 18 | ``` 19 | -------------------------------------------------------------------------------- /db_none.c: -------------------------------------------------------------------------------- 1 | #define DB_IMPL_NAME "None" 2 | 3 | typedef struct { 4 | Display *display; 5 | Window window; 6 | GC gc; 7 | } DB; 8 | 9 | void db_init(DB *db, Display *display, Window window) 10 | { 11 | db->display = display; 12 | db->window = window; 13 | db->gc = XCreateGC(display, window, 0, NULL); 14 | } 15 | 16 | void db_clear(DB *db) 17 | { 18 | XClearArea(db->display, db->window, 0, 0, WIDTH, HEIGHT, False); 19 | } 20 | 21 | void db_fill_rect(DB *db, int x, int y, unsigned int w, unsigned int h) 22 | { 23 | XSetForeground(db->display, db->gc, 0xFF0000); 24 | XFillRectangle(db->display, db->window, db->gc, x, y, w, h); 25 | } 26 | 27 | #define db_swap_buffers(...) 28 | -------------------------------------------------------------------------------- /db_pixmap.c: -------------------------------------------------------------------------------- 1 | #define DB_IMPL_NAME "Pixmap" 2 | 3 | typedef struct { 4 | Display *display; 5 | Window window; 6 | GC gc; 7 | Pixmap back_buffer; 8 | } DB; 9 | 10 | void db_init(DB *db, Display *display, Window window) 11 | { 12 | db->display = display; 13 | db->window = window; 14 | db->gc = XCreateGC(display, window, 0, NULL); 15 | db->back_buffer = XCreatePixmap(display, window, WIDTH, HEIGHT, 24); 16 | } 17 | 18 | void db_clear(DB *db) 19 | { 20 | XSetForeground(db->display, db->gc, 0); 21 | XFillRectangle(db->display, db->back_buffer, db->gc, 0, 0, WIDTH, HEIGHT); 22 | } 23 | 24 | void db_fill_rect(DB *db, int x, int y, unsigned int w, unsigned int h) 25 | { 26 | XSetForeground(db->display, db->gc, 0xFF0000); 27 | XFillRectangle(db->display, db->back_buffer, db->gc, x, y, w, h); 28 | } 29 | 30 | void db_swap_buffers(DB *db) 31 | { 32 | XCopyArea(db->display, 33 | db->back_buffer, 34 | db->window, 35 | db->gc, 36 | 0, 0, 37 | WIDTH, HEIGHT, 38 | 0, 0); 39 | } 40 | -------------------------------------------------------------------------------- /db_xdbe.c: -------------------------------------------------------------------------------- 1 | #define DB_IMPL_NAME "Xdbe" 2 | 3 | typedef struct { 4 | Display *display; 5 | Window window; 6 | GC gc; 7 | XdbeBackBuffer back_buffer; 8 | } DB; 9 | 10 | void db_init(DB *db, Display *display, Window window) 11 | { 12 | int major_version_return, minor_version_return; 13 | if(XdbeQueryExtension(display, &major_version_return, &minor_version_return)) { 14 | printf("XDBE version %d.%d\n", major_version_return, minor_version_return); 15 | } else { 16 | fprintf(stderr, "XDBE is not supported!!!1\n"); 17 | exit(1); 18 | } 19 | 20 | db->display = display; 21 | db->window = window; 22 | db->gc = XCreateGC(display, window, 0, NULL); 23 | db->back_buffer = XdbeAllocateBackBufferName(display, window, 0); 24 | } 25 | 26 | void db_clear(DB *db) 27 | { 28 | XSetForeground(db->display, db->gc, 0); 29 | XFillRectangle(db->display, db->back_buffer, db->gc, 0, 0, WIDTH, HEIGHT); 30 | } 31 | 32 | void db_fill_rect(DB *db, int x, int y, unsigned int w, unsigned int h) 33 | { 34 | XSetForeground(db->display, db->gc, 0xFF0000); 35 | XFillRectangle(db->display, db->back_buffer, db->gc, x, y, w, h); 36 | } 37 | 38 | void db_swap_buffers(DB *db) 39 | { 40 | XdbeSwapInfo swap_info; 41 | swap_info.swap_window = db->window; 42 | swap_info.swap_action = 0; 43 | XdbeSwapBuffers(db->display, &swap_info, 1); 44 | } 45 | -------------------------------------------------------------------------------- /db_ximage.c: -------------------------------------------------------------------------------- 1 | #define DB_IMPL_NAME "XImage" 2 | 3 | typedef struct { 4 | Display *display; 5 | Window window; 6 | GC gc; 7 | uint32_t *pixels; 8 | XImage *image; 9 | } DB; 10 | 11 | void db_init(DB *db, Display *display, Window window) 12 | { 13 | db->display = display; 14 | db->window = window; 15 | db->gc = XCreateGC(display, window, 0, NULL); 16 | db->pixels = malloc(sizeof(uint32_t) * WIDTH * HEIGHT); 17 | 18 | XWindowAttributes wa = {0}; 19 | XGetWindowAttributes(display, window, &wa); 20 | 21 | db->image = XCreateImage(display, 22 | wa.visual, 23 | wa.depth, 24 | ZPixmap, 25 | 0, 26 | (char*) db->pixels, 27 | WIDTH, HEIGHT, 28 | 32, 29 | WIDTH * sizeof(uint32_t)); 30 | } 31 | 32 | void db_clear(DB *db) 33 | { 34 | memset(db->pixels, 0, sizeof(uint32_t) * WIDTH * HEIGHT); 35 | } 36 | 37 | void db_fill_rect(DB *db, int x0, int y0, unsigned int w, unsigned int h) 38 | { 39 | for (unsigned int dx = 0; dx < w; ++dx) { 40 | for (unsigned int dy = 0; dy < h; ++dy) { 41 | int x = x0 + dx; 42 | int y = y0 + dy; 43 | 44 | if (0 <= x && (unsigned int) x < WIDTH && 45 | 0 <= y && (unsigned int) y < HEIGHT) { 46 | db->pixels[y*WIDTH + x] = 0xFF0000; 47 | } 48 | } 49 | } 50 | } 51 | 52 | void db_swap_buffers(DB *db) 53 | { 54 | XPutImage(db->display, db->window, db->gc, db->image, 0, 0, 0, 0, WIDTH, HEIGHT); 55 | } 56 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #define WIDTH 800 13 | #define HEIGHT 600 14 | #define FPS 120 15 | #define RECT_WIDTH 300 16 | #define RECT_HEIGHT 300 17 | 18 | #define DB_NONE 0 19 | #define DB_XDBE 1 20 | #define DB_PIXMAP 2 21 | #define DB_XIMAGE 3 22 | 23 | #if DB_IMPL == DB_NONE 24 | # include "./db_none.c" 25 | #elif DB_IMPL == DB_XDBE 26 | # include "./db_xdbe.c" 27 | #elif DB_IMPL == DB_PIXMAP 28 | # include "./db_pixmap.c" 29 | #elif DB_IMPL == DB_XIMAGE 30 | # include "./db_ximage.c" 31 | #else 32 | # error "Unsupported Double Buffering approach" 33 | #endif 34 | 35 | int main(void) 36 | { 37 | printf("Double Buffering Implementation: "DB_IMPL_NAME"\n"); 38 | 39 | DB db = {0}; 40 | 41 | Display *display = XOpenDisplay(NULL); 42 | if (display == NULL) { 43 | fprintf(stderr, "ERROR: could not open the default display\n"); 44 | exit(1); 45 | } 46 | 47 | Window window = XCreateSimpleWindow( 48 | display, 49 | XDefaultRootWindow(display), 50 | 0, 0, 51 | WIDTH, HEIGHT, 52 | 0, 53 | 0, 54 | 0); 55 | 56 | db_init(&db, display, window); 57 | 58 | XStoreName(display, window, "DB Implementation: "DB_IMPL_NAME); 59 | 60 | Atom wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False); 61 | XSetWMProtocols(display, window, &wm_delete_window, 1); 62 | 63 | XSelectInput(display, window, KeyPressMask); 64 | 65 | XMapWindow(display, window); 66 | 67 | int rect_x = 10; 68 | int rect_y = 10; 69 | 70 | int rect_dx = -1; 71 | int rect_dy = -1; 72 | 73 | int quit = 0; 74 | while (!quit) { 75 | while (XPending(display) > 0) { 76 | XEvent event = {0}; 77 | XNextEvent(display, &event); 78 | switch (event.type) { 79 | case KeyPress: { 80 | switch (XLookupKeysym(&event.xkey, 0)) { 81 | case 'q': 82 | quit = 1; 83 | break; 84 | default: 85 | {} 86 | } 87 | } break; 88 | case ClientMessage: { 89 | if ((Atom) event.xclient.data.l[0] == wm_delete_window) { 90 | quit = 1; 91 | } 92 | } 93 | break; 94 | } 95 | } 96 | 97 | db_clear(&db); 98 | db_fill_rect(&db, rect_x, rect_y, RECT_WIDTH, RECT_HEIGHT); 99 | db_swap_buffers(&db); 100 | 101 | int rect_nx = rect_x + rect_dx; 102 | if (rect_nx <= 0 || rect_nx + RECT_WIDTH >= WIDTH) { 103 | rect_dx *= -1; 104 | } else { 105 | rect_x = rect_nx; 106 | } 107 | 108 | int rect_ny = rect_y + rect_dy; 109 | if (rect_ny <= 0 || rect_ny + RECT_HEIGHT >= HEIGHT) { 110 | rect_dy *= -1; 111 | } else { 112 | rect_y = rect_ny; 113 | } 114 | 115 | usleep(1000*1000/FPS); 116 | } 117 | 118 | XCloseDisplay(display); 119 | return 0; 120 | } 121 | --------------------------------------------------------------------------------