aboutsummaryrefslogtreecommitdiff
path: root/src/mgl.c
blob: 02fe8a5968035185887c803a213dbea19763ce0a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include "../include/mgl/mgl.h"
#include <X11/Xutil.h>
#include <stdio.h>
#ifndef NDEBUG
#include <stdlib.h>
#endif

static mgl_context context;
static int init_count = 0;
static XErrorHandler prev_xerror = NULL;
static XIOErrorHandler prev_xioerror = NULL;

static int ignore_xerror(Display *display, XErrorEvent *ee) {
    (void)display;
    (void)ee;
    return 0;
}

static int ignore_xioerror(Display *display) {
    (void)display;
    return 0;
}

static int glx_context_init() {
    const int attr[] = {
        GLX_RGBA,
        GLX_BUFFER_SIZE, 24,
        GLX_DEPTH_SIZE, 0,
        GLX_STENCIL_SIZE, 0,
        GLX_RED_SIZE, 8,
        GLX_GREEN_SIZE, 8,
        GLX_BLUE_SIZE, 8,
        GLX_ALPHA_SIZE, 0,
        GLX_ACCUM_RED_SIZE, 0,
        GLX_ACCUM_GREEN_SIZE, 0,
        GLX_ACCUM_BLUE_SIZE, 0,
        GLX_ACCUM_ALPHA_SIZE, 0,
        GLX_DOUBLEBUFFER, /* TODO: Add option to turn this off? */
        None
    };

    context.visual_info = context.gl.glXChooseVisual(context.connection, DefaultScreen(context.connection), (int*)attr);
    if(!context.visual_info) {
        fprintf(stderr, "glXChooseVisual failed, no appropriate visual found\n");
        return -1;
    }

    return 0;
}

static void glx_context_deinit() {
    if(context.visual_info) {
        XFree(context.visual_info);
        context.visual_info = NULL;
    }
}

int mgl_init(void) {
    ++init_count;
    if(init_count == 1) {
        context.connection = XOpenDisplay(NULL);
        if(!context.connection) {
            fprintf(stderr, "XOpenDisplay(NULL) failed\n");
            mgl_deinit();
            return -1;
        }

        prev_xerror = XSetErrorHandler(ignore_xerror);
        prev_xioerror = XSetIOErrorHandler(ignore_xioerror);

        context.wm_delete_window_atom = XInternAtom(context.connection, "WM_DELETE_WINDOW", False);
        if(!context.wm_delete_window_atom) {
            mgl_deinit();
            return -1;
        }

        context.net_wm_ping_atom = XInternAtom(context.connection, "_NET_WM_PING", True);

        if(mgl_gl_load(&context.gl) != 0) {
            mgl_deinit();
            return -1;
        }

        if(glx_context_init() != 0) {
            mgl_deinit();
            return -1;
        }
    }
    return 0;
}

void mgl_deinit(void) {
    if(init_count == 1) {
        if(context.connection) {
            XSetIOErrorHandler(prev_xioerror);
            prev_xioerror = NULL;

            XSetErrorHandler(prev_xerror);
            prev_xerror = NULL;

            XCloseDisplay(context.connection);
            context.connection = NULL;

            /*
                GLX needs to be unloaded after closing the display on nvidia because
                nvidia registers cleanup callbacks on exit, that uses the x11 display.
            */
            glx_context_deinit();
            mgl_gl_unload(&context.gl);
        }
    }

    if(init_count > 0)
        --init_count;
}

mgl_context* mgl_get_context(void) {
#ifndef NDEBUG
    if(init_count == 0) {
        fprintf(stderr, "Error: mgl_get_context was called before mgl_init\n");
        abort();
    }
#endif
    return &context;
}