aboutsummaryrefslogtreecommitdiff
path: root/src/window/window_x11.c
blob: 55b0a75c84122c4aa385b439512e414629f34d47 (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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include "../../include/window/window_x11.h"

#include "../../include/vec2.h"
#include "../../include/defs.h"
#include "../../include/utils.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <X11/Xlib.h>

#define GSR_MAX_OUTPUTS 32

typedef struct {
    char *name;
    vec2i pos;
    vec2i size;
    uint32_t connector_id;
    gsr_monitor_rotation rotation;
    uint32_t monitor_identifier; /* crtc id */
} gsr_x11_output;

typedef struct {
    Display *display;
    Window window;
    gsr_x11_output outputs[GSR_MAX_OUTPUTS];
    int num_outputs;
    XEvent xev;
} gsr_window_x11;

static void store_x11_monitor(const gsr_monitor *monitor, void *userdata) {
    gsr_window_x11 *window_x11 = userdata;
    if(window_x11->num_outputs == GSR_MAX_OUTPUTS) {
        fprintf(stderr, "gsr warning: reached maximum outputs (%d), ignoring output %s\n", GSR_MAX_OUTPUTS, monitor->name);
        return;
    }

    char *monitor_name = strdup(monitor->name);
    if(!monitor_name)
        return;

    const int index = window_x11->num_outputs;
    window_x11->outputs[index].name = monitor_name;
    window_x11->outputs[index].pos = monitor->pos;
    window_x11->outputs[index].size = monitor->size;
    window_x11->outputs[index].connector_id = monitor->connector_id;
    window_x11->outputs[index].rotation = monitor->rotation;
    window_x11->outputs[index].monitor_identifier = monitor->monitor_identifier;
    ++window_x11->num_outputs;
}

static void gsr_window_x11_deinit(gsr_window_x11 *self) {
    if(self->window) {
        XDestroyWindow(self->display, self->window);
        self->window = None;
    }

    for(int i = 0; i < self->num_outputs; ++i) {
        if(self->outputs[i].name) {
            free(self->outputs[i].name);
            self->outputs[i].name = NULL;
        }
    }
    self->num_outputs = 0;
}

static bool gsr_window_x11_init(gsr_window_x11 *self) {
    self->window = XCreateWindow(self->display, DefaultRootWindow(self->display), 0, 0, 16, 16, 0, CopyFromParent, InputOutput, CopyFromParent, 0, NULL);
    if(!self->window) {
        fprintf(stderr, "gsr error: gsr_window_x11_init failed: failed to create gl window\n");
        return false;
    }

    self->num_outputs = 0;
    for_each_active_monitor_output_x11_not_cached(self->display, store_x11_monitor, self);
    return true;
}

static void gsr_window_x11_destroy(gsr_window *window) {
    gsr_window_x11 *self = window->priv;
    gsr_window_x11_deinit(self);
    free(self);
    free(window);
}

static bool gsr_window_x11_process_event(gsr_window *window) {
    gsr_window_x11 *self = window->priv;
    if(XPending(self->display)) {
        XNextEvent(self->display, &self->xev);
        return true;
    }
    return false;
}

static XEvent* gsr_window_x11_get_event_data(gsr_window *window) {
    gsr_window_x11 *self = window->priv;
    return &self->xev;
}

static gsr_display_server gsr_window_x11_get_display_server(void) {
    return GSR_DISPLAY_SERVER_X11;
}

static void* gsr_window_x11_get_display(gsr_window *window) {
    gsr_window_x11 *self = window->priv;
    return self->display;
}

static void* gsr_window_x11_get_window(gsr_window *window) {
    gsr_window_x11 *self = window->priv;
    return (void*)self->window;
}

static void gsr_window_x11_for_each_active_monitor_output_cached(const gsr_window *window, active_monitor_callback callback, void *userdata) {
    const gsr_window_x11 *self = window->priv;
    for(int i = 0; i < self->num_outputs; ++i) {
        const gsr_x11_output *output = &self->outputs[i];
        const gsr_monitor monitor = {
            .name = output->name,
            .name_len = strlen(output->name),
            .pos = output->pos,
            .size = output->size,
            .connector_id = output->connector_id,
            .rotation = output->rotation,
            .monitor_identifier = output->monitor_identifier
        };
        callback(&monitor, userdata);
    }
}

gsr_window* gsr_window_x11_create(Display *display) {
    gsr_window *window = calloc(1, sizeof(gsr_window));
    if(!window)
        return window;

    gsr_window_x11 *window_x11 = calloc(1, sizeof(gsr_window_x11));
    if(!window_x11) {
        free(window);
        return NULL;
    }

    window_x11->display = display;
    if(!gsr_window_x11_init(window_x11)) {
        free(window_x11);
        free(window);
        return NULL;
    }

    *window = (gsr_window) {
        .destroy = gsr_window_x11_destroy,
        .process_event = gsr_window_x11_process_event,
        .get_event_data = gsr_window_x11_get_event_data,
        .get_display_server = gsr_window_x11_get_display_server,
        .get_display = gsr_window_x11_get_display,
        .get_window = gsr_window_x11_get_window,
        .for_each_active_monitor_output_cached = gsr_window_x11_for_each_active_monitor_output_cached,
        .priv = window_x11
    };

    return window;
}