SYNOPSIS

gcc -Wall -g -o open_window -lX11 -lGL open_window.c
./open_window

DESCRIPTION

A simple library and test code.

#include <X11/Xlib-xcb.h>
#include <X11/Xlib.h>
extern long keysym2ucs(KeySym keysym);
#include "keysym2ucs.c"
#include <xcb/xcb.h>
#include <xcb/xcb_event.h>
#include <xcb/xcb_keysyms.h>
#include <xcb/xproto.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ftgl.h>

#ifndef USE_LIBEV
#define USE_LIBEV 1
#endif
#ifndef USE_POLL
#define USE_POLL 0
#endif
#ifndef DEBUG_WINDOW_GEOMETRY
#define DEBUG_WINDOW_GEOMETRY 1
#endif

#if USE_POLL
#include <poll.h>
#else
#include <sys/select.h>
#endif
#if USE_LIBEV
#include <ev.h>
#endif

/* DEFINES */
#ifndef DO_DEBUG_EVENT_LOOP
#define DO_DEBUG_EVENT_LOOP 1
#endif
#if (DO_DEBUG_EVENT_LOOP&1)
static int DRI2EventBase=0;
#endif

#define NUM_POLL_CHANNELS 5

/* PROTOTYPES */
struct list_of_windows; /* each display stores a double linked list of windows */
struct poll_channels {
#if USE_POLL
        struct pollfd channel[NUM_POLL_CHANNELS];
#else
        fd_set fds[3*NUM_POLL_CHANNELS];
#endif
        int nfds;
        int timeout;
};
struct display {
        Display* handle;
        xcb_connection_t* connection;
        xcb_screen_t* screen;
        xcb_generic_event_t* event;
        struct list_of_windows_el* current_window;
        struct list_of_windows* all_windows;
#if USE_LIBEV
        struct ev_loop* event_loop;
        struct watch_display_t {
                ev_io displayio;
                struct display* backref;
        } watch_display;
        ev_timer timer;
#endif
        int (*loop)(struct display*);
        int (*select_window)(struct display*, int win);
};
struct info {
        int version;
        GLXFBConfig config;
        XVisualInfo* visual;
        xcb_colormap_t colormap;
        struct poll_channels channels;
        xcb_key_symbols_t* keysymbols;
};
struct geometry {
        float x,y,w,h;
};
struct drawctxt {
        GLXContext context;
        GLXDrawable drawable;
        GLXWindow window;
};
struct context {
        struct drawctxt* gl;
        void* attached;
};
enum window_modes { wm_size_changed=1, wm_new_context=2 };
struct window {
        struct display* display;
        xcb_window_t handle;
        struct info* info;
        struct context ctxt;
        struct geometry geom;
        int mode;
        int keyflags;
        int (*open)(struct window* w);
        int (*draw)(struct window* w);
};

Window Lists

We might want to be able to handle several windows for one connection.

struct list_of_windows_el {
        struct list_of_windows_el *prev, *next;
        int mode;
        struct window window;
};
enum list_modes { lm_sorted=0, lm_list };
enum list_element_mode { lem_data_set=0
};
struct list_of_windows {
        struct list_of_windows_el *head;
        unsigned elsize, space, count;
        enum list_modes mode;
};
struct list_return { void* base; void* el; };
struct any_list { void* head; unsigned elsize; unsigned space; unsigned count;
        int mode; };
struct any_list_element { struct any_list_element* prev, *next;
        int mode;
};

#define COPY_LIST_ELEMENT(SRC,DEST,SIZE) do { \
        char* dp = (char*)&(DEST->mode); \
        char* sp = (char*)&(SRC->mode); \
        int ofs, len; \
        dp += sizeof(DEST->mode); \
        sp += sizeof(SRC->mode); \
        ofs = dp-((char*)DEST); \
        len = SIZE, len = (len>ofs) ?len-ofs :0; \
        DEST->mode |= (1<<lem_data_set); \
        /* printf("%p %p %d\n", dp, sp, len); */ \
        memcpy(dp,sp,len); \
} while(0)

extern void* new_list(unsigned n_elements, unsigned size);
#define LIST_FUNC(N) struct list_return N(void* list,\
        void* element, int(*compar)(void*a,void*b))
extern LIST_FUNC(search_element);
extern LIST_FUNC(add_element);
extern LIST_FUNC(list_mode);
extern LIST_FUNC(array_mode);

static int compare_window_elements(void* a, void* b);
#define ADD_WINDOW(DISPLAY,WINDOW) do \
{ \
        struct list_of_windows_el W; \
        memset(&W,0,sizeof(W)); \
        W.window = WINDOW; \
        DISPLAY->current_window = add_element(DISPLAY->all_windows,&W,compare_window_elements).el;\
} \
while(0)

Extensions Query

We might want to react on extension events. So we need the configuration.

struct query_extensions_t {
        xcb_query_extension_cookie_t ext;
        xcb_query_extension_reply_t* repl;
        const char* name; int nameLen;
};
static void init_extension(xcb_connection_t* c, struct query_extensions_t *qext);
void init_extension(xcb_connection_t* c, struct query_extensions_t *qext)
{
        xcb_generic_error_t* err;
        qext->repl = xcb_query_extension_reply(c, qext->ext,&err);
        if (err) {
                fputs("failed to query extension ",stdout);
                fwrite(qext->name,qext->nameLen,1,stdout);
                fputc('\n',stdout);
        }
        else {
                fputs("Extension ",stdout);
                fwrite(qext->name,qext->nameLen,1,stdout);
                fprintf(stdout," parameters (op=%3d, ev=%3d, err=%3d)\n",
                        qext->repl->major_opcode,
                        qext->repl->first_event,
                        qext->repl->first_error);
#if (DO_DEBUG_EVENT_LOOP&1)
                if (0==(memcmp(qext->name,"DRI2",qext->nameLen<4?qext->nameLen:4)))
                        DRI2EventBase=qext->repl->first_event;
#endif
        }
}

Functions

Some functions that are exported by this module.

static int open_x11_window(struct window* win);
static int std_x11_loop(struct display* display);
#if USE_LIBEV
static void std_x11_event(struct ev_loop* L, struct ev_io* w, int revents);
typedef struct { unsigned char code[8]; } utf8_t;
static utf8_t get_utf8_from_ucs(uint32_t cp);
#endif
static int test_draw(struct window* win);

static int wait_for_event(struct display* d);

static int select_window(struct display* display, int win);

The Main function

This function contains the test code for the library functions and is not thought to be exported if a library is build from this source.

/* static struct display* new_display(); */
/* static struct window* new_window(); */
static struct info* new_info();
static struct drawctxt* new_drawctxt();
int main(int argc, char** argv)
{
#if (TEST_LIST>0)
        struct window win;
        struct display* D = new_display();
        int i;

        D->all_windows = new_list(10,sizeof(struct window));
        memset(&win,0x0,sizeof(win));
        win.display = D;
        for (i=0;i<10;i++) {
                win.handle = i; ADD_WINDOW(D,win);
        }
        for (i=0; i<D->all_windows->count; ++i) {
                int handle = (int)(D->all_windows->head[i].window.handle);
                printf("%3d: %3d\n", i, handle);
        }
#else
        int i,n;
        int minKey, maxKey;
        Display* D;
        const char* dn;
        GLXFBConfig* cfgs;
        struct window win;
        struct display display;
        int defScreen;

        memset(&win,0,sizeof(win));
        memset(&display,0,sizeof(display));
        win.display = &display;
        win.display->select_window = select_window;
        win.display->all_windows = new_list(2,sizeof(struct list_of_windows_el));
        win.info = new_info();
        win.draw = test_draw;
        win.ctxt.gl = new_drawctxt();
        win.open = open_x11_window;
        win.display->loop = wait_for_event;
        D = win.display->handle = XOpenDisplay(dn=getenv("DISPLAY"));
        if (!D) {
                printf("cannot connect to display \"%s\"\n",
                        dn ?dn :"(null)");
                exit(-1);
        }
        win.display->connection = XGetXCBConnection(D);
        XSetEventQueueOwner(D,XCBOwnsEventQueue);
        win.display->watch_display.backref = win.display;
        ev_io_init(
                &win.display->watch_display.displayio,
                std_x11_event,
                xcb_get_file_descriptor(win.display->connection),
                EV_READ);
        win.display->event_loop = ev_default_loop(0);
        ev_io_start(win.display->event_loop, &win.display->watch_display.displayio);
        XDisplayKeycodes(D, &minKey, &maxKey);
        win.info->keysymbols = xcb_key_symbols_alloc(win.display->connection);
        printf("Display Keycodes [%02x,%02x]\n", minKey, maxKey);

{
        enum {qext_num=32};
        uint32_t fill, num=qext_num, pos;
        struct query_extensions_t qext[qext_num];
        xcb_list_extensions_cookie_t list;
        xcb_list_extensions_reply_t *lr;
        xcb_generic_error_t *err;
        xcb_str_iterator_t name_i;
        int go = 1;
        list = xcb_list_extensions(win.display->connection);
        lr = xcb_list_extensions_reply(win.display->connection,
                list, &err);
        if (!err) go=1; else go=0;
        num=32;
        if (go) {
                name_i = xcb_list_extensions_names_iterator(lr);
        }
        fill=pos=0;
        while (go) {
                go = (name_i.data != xcb_str_end(name_i).data);
                if (go) {
                        qext[fill].name = xcb_str_name(name_i.data);
                        qext[fill].nameLen = xcb_str_name_length(name_i.data);
                        qext[fill].ext = xcb_query_extension(
                                win.display->connection,
                                qext[fill].nameLen, qext[fill].name);
                        xcb_str_next(&name_i);
                        ++fill;
                }
                if (fill==num) { fill=0; for(pos=0;pos<num;++pos) {
                        init_extension(win.display->connection,&qext[pos]);
                        if (qext[pos].repl) free(qext[pos].repl);
                }}
        }
        if (fill>0) for(pos=0;pos<fill;++pos){
                init_extension(win.display->connection,&qext[pos]);
                if (qext[pos].repl) free(qext[pos].repl);
        }
        free(lr);
}

        defScreen = DefaultScreen(D);
        xcb_screen_iterator_t screen_iter =
                xcb_setup_roots_iterator(xcb_get_setup(win.display->connection));
        for(i = defScreen;
                screen_iter.rem && i > 0;
                --i, xcb_screen_next(&screen_iter));
        win.display->screen = screen_iter.data;
        win.geom.w = (float)win.display->screen->width_in_pixels;
        win.geom.h = (float)win.display->screen->height_in_pixels;
{
        int hi, lo;
        if ( False == (glXQueryVersion(D, &hi, &lo)) ) {
                printf("Failed to query GLX for Display: %p\n", D);
                return -1;
        }
        win.info->version = (hi<<4) | (lo&0xf);
        printf("GLX %d.%d\n", hi, lo);
}
        printf("GLX Version: %d\n", win.info->version);

{
        int attr[] = { GLX_DOUBLEBUFFER, True,
                GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
                GLX_X_RENDERABLE, True,
                None
        };
        cfgs = glXChooseFBConfig(D,0,attr,&n);
        if (n>0) {
                win.info->config = cfgs[0];
                win.info->visual = glXGetVisualFromFBConfig(D,win.info->config);
        }
        XFree(cfgs);
}
        printf("found %d configs\n", n);
        win.ctxt.gl->context = glXCreateNewContext(D,win.info->config, GLX_RGBA_TYPE, 0, True);
        win.mode |= wm_new_context;
        win.info->colormap = xcb_generate_id(win.display->connection);
        xcb_create_colormap(win.display->connection, XCB_COLORMAP_ALLOC_NONE,
                win.info->colormap, win.display->screen->root,
                win.info->visual->visualid);
        win.open(&win);
        ADD_WINDOW(win.display,win);
        win.ctxt.gl->window = win.handle;
        glXMakeContextCurrent(D,win.ctxt.gl->window,win.ctxt.gl->window,win.ctxt.gl->context);
        win.ctxt.gl->drawable = win.ctxt.gl->window;
        win.display->loop(win.display);
#endif
        return 0;
}

Creating Types

In general a type can be created by calloc(1,sizeof(T)).

#define new_F(N,TN) TN* new_ ## N() { \
        TN* obj = calloc(1,sizeof(TN)); return obj; }\

#define new_SF(N) new_F(N,struct N)

/* new_SF(display) */
new_SF(info)
/* new_SF(window) */
new_SF(drawctxt)

X11 Implementation — open

Window handle creation in an X11 System.

#define GEOM(G) (int)(G.x+.5), (int)(G.y+.5),\
(unsigned int)(G.w+.5), (unsigned int)(G.h+.5)
int open_x11_window(struct window* win)
{
        uint32_t eventmask = XCB_EVENT_MASK_EXPOSURE|XCB_EVENT_MASK_KEY_PRESS|\
                XCB_EVENT_MASK_STRUCTURE_NOTIFY;
        uint32_t valuelist[] = { XCB_NONE, eventmask, win->info->colormap, 0 };
        uint32_t valuemask = XCB_CW_EVENT_MASK|XCB_CW_COLORMAP|\
                XCB_CW_BACK_PIXMAP;
        xcb_void_cookie_t cookies[2];
        xcb_connection_t* c = win->display->connection;
        xcb_generic_error_t *err;
        if (win->handle != 0x0) return win->handle;

        win->handle = xcb_generate_id(win->display->connection);
        cookies[0] = xcb_create_window(win->display->connection,
                XCB_COPY_FROM_PARENT, win->handle, win->display->screen->root,
                GEOM(win->geom), 0,
                XCB_WINDOW_CLASS_INPUT_OUTPUT, win->info->visual->visualid,
                valuemask, valuelist);
        cookies[1] = xcb_map_window(win->display->connection, win->handle);
        if (0x0 != (err=xcb_request_check(c,cookies[0]))) {
                printf("request (%03d,%03d) failed with error %3d\n",
                        err->pad0, err->major_code, err->error_code);
                return 1;
        }
        if (0x0 != (err=xcb_request_check(c,cookies[1]))) {
                printf("request (%03d,%03d) failed with error %3d\n",
                        err->pad0, err->major_code, err->error_code);
                return 2;
        }
        return 0;
}

Test implementation

The test implementation of the window draw function, also needs some special context data, that is initialized on demand.

enum test_context_numbers { tc_num_textures=1 };
struct test_context
{
        FTGLfont* font;
        char* font_path;
        GLuint texture[tc_num_textures];
        int status;
};
int test_draw(struct window* w)
{
        static int cmode=0;
        static FTGLfont *font=0x0;
        static const char* font_path="font.ttf";
        if ( font==0x0 ) {
                font = ftglCreateTextureFont(font_path);
        }
        switch (cmode) {
                case 0: glClearColor(.0f,.5f,0.8f,0.5f); break;
                case 1: glClearColor(.8f,.5f,.0f,.5f); break;
                case 2: glClearColor(.5f,.8f,.0f,.5f); break;
                case 3: glClearColor(.5f,.0f,.8f,.5f); break;
        }
        cmode = (cmode>2)?0:cmode+1;
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(-1000,1000,1000,-1000,0,1);
        glColor4f(.0f,.0f,.0f,.0f);
        glBegin(GL_QUADS);

        glVertex3f(10.0f,10.0f,.0f);
        glVertex3f(400.0f,10.0f,.0f);
        glVertex3f(400.0f,400.0f,.0f);
        glVertex3f(10.0f,400.0f,.0f);

        glEnd();
        glXSwapBuffers(w->display->handle, w->ctxt.gl->window);
        return 0;
}

Standard loop

The standard loop is used when there are no other input sources. Later, this might be modified to poll from other filedescriptors too.

#if (DO_DEBUG_EVENT_LOOP&1)
#define DEBUG_EVENT_LOOP_EVENT(E,display) do \
        { \
                static const char* event_name_table[0x7f] = { \
                        0x0, 0x0 \
                }; \
                const char* event_name = event_name_table[0]; \
                if ( event_name_table[0]==event_name_table[1] ) { \
                        int s,n,l; \
                        event_name_table[0] = "unknown"; \
                        n=(sizeof(event_name_table)/sizeof(const char*)); \
                        for (l=0,s=1; s<n; s+=l) { \
                                if (l==0)l=1; \
                                else { \
                                        l*=2; \
                                        if (s+l>n) l=n-s; \
                                } \
                                memcpy(event_name_table+s,event_name_table,l*sizeof(const char*)); \
                        } \
                        event_name_table[0] = "error"; \
                        event_name_table[1] = "reply"; \
                        event_name_table[0xc] = "expose"; \
                        event_name_table[0x2] = "key-press"; \
                        event_name_table[MapNotify] = "map"; \
                        event_name_table[UnmapNotify] = "unmap"; \
                        event_name_table[ConfigureNotify] = "configure"; \
                        if (DRI2EventBase!=0) { \
                                event_name_table[DRI2EventBase] = "DRI2 Swap Complete"; \
                                event_name_table[DRI2EventBase+1] = "DRI2 Invalidate Buffers"; \
                        } \
                } \
                event_name = event_name_table[E->response_type & ~0x80]; \
                fprintf(stderr, "event:%20s(%02d) %d\n", event_name?event_name:"*FATAL*", E->response_type, \
                        xcb_connection_has_error(display->connection)); \
        } while (0)
#else
#define DEBUG_EVENT_LOOP_EVENT(E,display) do { } while(0)
#endif

#if USE_LIBEV
void std_x11_event(struct ev_loop* L, struct ev_io* w, int revents)
{
        struct watch_display_t *x = (struct watch_display_t*)w;
        struct display* d = x->backref;
        std_x11_loop(d);
}
#endif
#define STDX11LOOP_SelectWindow(WIN) do { \
        if(cwh!=WIN) { \
                if (0!=change_window(display,WIN)) { \
                        printf("the window %d not found\n", WIN); \
                        return -1; \
                } \
                cw = display->current_window; \
        } \
} while(0)
#define SQR(X) ((X)*(X))
#define GEOM_DELTA(A,B)  \
        (SQR(A.x-B.x)+SQR(A.y-B.y)+SQR(A.w-B.w)+SQR(A.h-B.h))
int std_x11_loop(struct display* display)
{
        int running = 1;
        struct list_of_windows_el* cw;
        struct window* win;
        int cwh;
        xcb_generic_event_t*E;
        int (*change_window)(struct display* display, int w);

        if (!display) { printf("no display structure to work on\n"); return -1; }
        cw = display->current_window;
        win = &cw->window;
        cwh = (cw) ?cw->window.handle:0x0;
        change_window = display->select_window;

        while(running) {
                E = display->event = xcb_poll_for_event(display->connection);
                if (!E) break;
                DEBUG_EVENT_LOOP_EVENT(E,display);
                switch (E->response_type & ~0x80) {
                        case XCB_KEY_PRESS:{
                                xcb_key_press_event_t* key =(xcb_key_press_event_t*)E;
                                xcb_keysym_t sym = xcb_key_symbols_get_keysym(
                                        win->info->keysymbols, key->detail, win->keyflags );
                                long ucs = keysym2ucs((KeySym)sym);
                                utf8_t utf8 = get_utf8_from_ucs(ucs);
                                utf8.code[0] = 0x0;
/*
                                printf("found ucs code x0%04x, %s, from symbol 0x%04x\n", (uint32_t)ucs, utf8.code, sym);
                                printf("DETAIL = 0x%04x\n", key->detail); */
                        } break;
                        case XCB_EXPOSE:{
                                struct geometry G;
                                float H;
                                struct window* W;
                                Display* D;
                                xcb_window_t win;
                                GLXContext ctxt;
                                xcb_expose_event_t* expose = (xcb_expose_event_t*)E;
                                int (*draw)(struct window*);
                                STDX11LOOP_SelectWindow(expose->window);
                                W = &cw->window;
                                D = W->display->handle;
                                win = W->handle;
                                ctxt = W->ctxt.gl->context;
                                G = W->geom;
                                draw = cw->window.draw;
                                printf("drawing (%d,%d %dx%d)...\n", GEOM(G));
                                H = W->display->screen->height_in_pixels;
                                if (G.h>H)G.h=H;
                                G.y = H - G.h;
                                G.x = .0f;
                                printf("viewport (%d,%d %dx%d)...\n", GEOM(G));
                                glViewport(G.x,G.y,G.w,G.h);
                                if (draw) draw(&cw->window);
                                printf("...done.\n");
                        } break;
                        case XCB_CONFIGURE_NOTIFY:{
                                Display* D;
                                GLXContext ctxt;
                                xcb_connection_t* c;
                                xcb_window_t win;
                                uint16_t mask;
                                uint32_t values[4];
                                struct geometry G,O;
                                xcb_configure_notify_event_t* cfg =
                                        (xcb_configure_notify_event_t*)E;
                                STDX11LOOP_SelectWindow(cfg->window);
                                win = cw->window.handle;
                                c = cw->window.display->connection;
                                D = cw->window.display->handle;
                                ctxt = cw->window.ctxt.gl->context;
                                mask = XCB_CONFIG_WINDOW_X|
                                        XCB_CONFIG_WINDOW_Y|
                                        XCB_CONFIG_WINDOW_WIDTH|
                                        XCB_CONFIG_WINDOW_HEIGHT;
                                G=cw->window.geom; O.x=(float)cfg->x;
                                O.y=(float)cfg->y; O.w=(float)cfg->width;
                                O.h=(float)cfg->height;
                                if(GEOM_DELTA(G,O)>.25f) {
#if DEBUG_WINDOW_GEOMETRY
                                        printf("configured window "
                                                "[(%d,%d %dx%d) - (%d,%d %dx%d)]² ="
                                                "%f\n", GEOM(G), GEOM(O), GEOM_DELTA(G,O));
#endif
                                        G=O; values[0]=(uint32_t)(G.x+.5f);
                                        values[1]=(uint32_t)(G.y+.5f);
                                        values[2]=(uint32_t)(G.w+.5f);
                                        values[3]=(uint32_t)(G.h+.5f);
                                        // xcb_configure_window(c,cw->window.ctxt.gl->window,mask,values);
                                        cw->window.geom = G;
                                        cw->window.mode|= wm_size_changed;
                                }
#if DEBUG_WINDOW_GEOMETRY&2
                                else {
                                        printf("not configured window "
                                                "[(%d,%d %dx%d) - (%d,%d %dx%d)]² ="
                                                "%f\n", GEOM(G), GEOM(O), GEOM_DELTA(G,O));
                                }
#endif
                        } break;
                        case 0: {
                                xcb_generic_error_t* err = (xcb_generic_error_t*)E;
                                fprintf(stderr, "got error code %03d\n"
                                        "error label: %-30s\n", err->error_code,
                                        xcb_event_get_error_label(err->error_code)
                                );
                        } break;
                }
                free(E); display->event = 0x0;
        }
        return 0;
}

UCS to UTF8 Converter

Convert a UCS Character to a UTF8 code.

utf8_t get_utf8_from_ucs(uint32_t cp)
{
        utf8_t ret={ {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff} };
        if (cp<=0x7f) {
                ret.code[0] = (unsigned char)cp;
                ret.code[1] = 0x0;
        }
        else if (cp<=0x7ff) {
                ret.code[0] = (0xc0|(cp>>6));
                ret.code[1] = (0x80|(cp&0x03f));
                ret.code[2] = 0x0;
        }
        else if (cp<=0xffff) {
                ret.code[0] = (0xe0|(cp>>12));
                ret.code[1] = (0x80|((cp>>6)&0x3f));
                ret.code[2] = (0x80|(cp&0x3f));
                ret.code[3] = 0x0;
        }
        else if (cp<=0x1fffff) {
                ret.code[0] = (0xf0|(cp>>18));
                ret.code[1] = (0x80|((cp>>12)&0x3f));
                ret.code[2] = (0x80|((cp>>6)&0x3f));
                ret.code[3] = (0x80|(cp&0x3f));
                ret.code[4] = 0x0;
        }
        else if (cp<=0x3ffffff) {
                ret.code[0] = (0xf8|(cp>>24));
                ret.code[1] = (0x80|(cp>>18));
                ret.code[2] = (0x80|((cp>>12)&0x3f));
                ret.code[3] = (0x80|((cp>>6)&0x3f));
                ret.code[4] = (0x80|(cp&0x3f));
                ret.code[5] = 0x0;
        }
        else if (cp<=0x7fffffff) {
                ret.code[0] = (0xfc|(cp>>30));
                ret.code[1] = (0x80|((cp>>24)&0x3f));
                ret.code[2] = (0x80|((cp>>18)&0x3f));
                ret.code[3] = (0x80|((cp>>12)&0x3f));
                ret.code[4] = (0x80|((cp>>6)&0x3f));
                ret.code[5] = (0x80|(cp&0x3f));
                ret.code[6] = 0x0;
        }
        return ret;
}

Waiting for Window Events and Timeouts

Most events have input sources, which can be polled from a set of file handles.

int wait_for_event(struct display* d)
{
        int ret = -1;
        static struct ev_loop* L = 0x0;
        if (!L) L=EV_DEFAULT;
        ev_run(L,0);
        ret = (int)ev_depth(L);
        return ret;
}

General List Operations

Some simple list operations on opaque list elements. Acutally we use an array of lists, to be able to convert the list into a sorted array, which can be searched by binary search.

void* new_list(unsigned n, unsigned l)
{
        enum { AnyList=sizeof(struct any_list), AnyEl=sizeof(struct any_list_element) };
        unsigned E = l+AnyEl;
        unsigned L = n*E + AnyList;
        void* space = calloc(1,L);
        struct any_list* List=space, ListObj= { ((char*)space)+sizeof(struct any_list), E, n*E, 0, 0 };
        *List = ListObj;
        return space;
}
LIST_FUNC(add_element)
{
        struct list_return ret = {0x0,0x0};
        char*ip,*dp,*ep;
        struct any_list *_l = list;
        struct any_list_element *_e;

        ip=dp=0x0; ep=((char*)_l->head)+_l->count*_l->elsize;
        if ( ((char*)_l->head+_l->space)<(ep+_l->elsize) )
                return ret;
        ret = search_element(list,element,compar);
        if (ret.el!=0x0) {ret.el=0x0; return ret;}
        if (ret.base == 0x0) {
                if (_l->head==0x0) return ret; /* {0,0} => can't add to void */
                _e = _l->head;
                if ((_e->mode & (1<<lem_data_set)) == 0) {
                        COPY_LIST_ELEMENT(((struct any_list_element*)element),_e,_l->elsize);
                        ret.el=_e;
                        _l->count++;
                }
                else {
                        ip=_l->head; dp=ip+_l->elsize;
                }
        }
        else {
                ip=ret.base; ip+=_l->elsize;
                dp=ip; dp+=_l->elsize;
                if ( ((char*)_l->head)+_l->space < ip ) ip = 0x0;
        }
        if (ip!=0x0) {
                _e = ((struct any_list_element*)ip);
                if (ep>ip) { memcpy(dp,ip,ep-ip); }
                COPY_LIST_ELEMENT(((struct any_list_element*)element),_e,_l->elsize);
                _l->count++;
        }
        return ret;
}

LIST_FUNC(search_element)
{
        struct list_return ret = {0x0,0x0};
        char* bp;
        unsigned m,l,r;
        int c;
        struct any_list* L=list;

        if (L->count==0) return ret;
        for (bp=L->head,l=0,r=L->count-1; 0<((r+1)-l); ) {
                m = (l+r)/2;
                ret.base=(L->head+m*L->elsize);
                c = compar(element,ret.base);
                if (c==0) { ret.el=ret.base; return ret; }
                else if (c<0) r=m-1;
                else l=m+1;
        }
        if (c<0) ret.base=(L->head+l*L->elsize);
        return ret;
}

int compare_window_elements(void* a, void* b)
{
        struct list_of_windows_el* A = a;
        struct list_of_windows_el* B = b;
        int i = A ?A->window.handle: 0;
        int j = B ?B->window.handle: 0;

        return (i<j) ?-1 :(i==j)? 0: +1;
}

LIST MODES

Depending on the usage of a data space, we might store the collection as a sorted array or as a double linked list. The transformation functions might be used to assure that wanted mode is enabled.

The list modes are

  • lm_list ― a double linked list.

  • lm_array — a sorted array.

LIST_FUNC(list_mode)
{
        struct list_return ret = {0x0,0x0};
        return ret;
}
LIST_FUNC(array_mode)
{
        struct list_return ret = {0x0,0x0};
        return ret;
}

SELECTING A WINDOW

Most applications will run only one window, but this code is a good use case for the list handler.

int select_window(struct display* d, int win)
{
        struct list_of_windows_el W;
        struct list_return res;
        W.window.handle=(xcb_window_t)win;
        res = search_element(d->all_windows, &W,compare_window_elements);
        d->current_window = res.el;
        return (res.el ?0 :1);
}