aboutsummaryrefslogtreecommitdiff
path: root/src/cursor.c
blob: 737c33bdf3417e275806aa5e61d4bdafb84c20c9 (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
#include "../include/cursor.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <X11/extensions/Xfixes.h>

static bool gsr_cursor_set_from_x11_cursor_image(gsr_cursor *self, XFixesCursorImage *x11_cursor_image) {
    uint8_t *cursor_data = NULL;
    uint8_t *out = NULL;

    if(!x11_cursor_image)
        goto err;
        
    if(!x11_cursor_image->pixels)
        goto err;

    self->hotspot.x = x11_cursor_image->xhot;
    self->hotspot.y = x11_cursor_image->yhot;
    self->egl->glBindTexture(GL_TEXTURE_2D, self->texture_id);

    self->size.x = x11_cursor_image->width;
    self->size.y = x11_cursor_image->height;
    const unsigned long *pixels = x11_cursor_image->pixels;
    cursor_data = malloc(self->size.x * self->size.y * 4);
    if(!cursor_data)
        goto err;
    out = cursor_data;
    /* Un-premultiply alpha */
    for(int y = 0; y < self->size.y; ++y) {
        for(int x = 0; x < self->size.x; ++x) {
            uint32_t pixel = *pixels++;
            uint8_t *in = (uint8_t*)&pixel;
            uint8_t alpha = in[3];
            if(alpha == 0)
                alpha = 1;

            *out++ = (unsigned)*in++ * 255/alpha;
            *out++ = (unsigned)*in++ * 255/alpha;
            *out++ = (unsigned)*in++ * 255/alpha;
            *out++ = *in++;
        }
    }

    self->egl->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, self->size.x, self->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, cursor_data);
    free(cursor_data);

    self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    self->egl->glBindTexture(GL_TEXTURE_2D, 0);
    XFree(x11_cursor_image);
    return true;

    err:
    self->egl->glBindTexture(GL_TEXTURE_2D, 0);
    if(x11_cursor_image)
        XFree(x11_cursor_image);
    return false;
}

int gsr_cursor_init(gsr_cursor *self, gsr_egl *egl, Display *display) {
    int x_fixes_error_base = 0;

    assert(egl);
    assert(display);
    memset(self, 0, sizeof(*self));
    self->egl = egl;
    self->display = display;

    self->x_fixes_event_base = 0;
    if(!XFixesQueryExtension(self->display, &self->x_fixes_event_base, &x_fixes_error_base)) {
        fprintf(stderr, "gsr error: gsr_cursor_init: your X11 server is missing the XFixes extension\n");
        gsr_cursor_deinit(self);
        return -1;
    }

    self->egl->glGenTextures(1, &self->texture_id);

    XFixesSelectCursorInput(self->display, DefaultRootWindow(self->display), XFixesDisplayCursorNotifyMask);
    gsr_cursor_set_from_x11_cursor_image(self, XFixesGetCursorImage(self->display));
    self->cursor_image_set = true;

    return 0;
}

void gsr_cursor_deinit(gsr_cursor *self) {
    if(!self->egl)
        return;

    if(self->texture_id) {
        self->egl->glDeleteTextures(1, &self->texture_id);
        self->texture_id = 0;
    }

    XFixesSelectCursorInput(self->display, DefaultRootWindow(self->display), 0);

    self->display = NULL;
    self->egl = NULL;
}

void gsr_cursor_update(gsr_cursor *self, XEvent *xev) {
    if(xev->type == self->x_fixes_event_base + XFixesCursorNotify) {
        XFixesCursorNotifyEvent *cursor_notify_event = (XFixesCursorNotifyEvent*)xev;
        if(cursor_notify_event->subtype == XFixesDisplayCursorNotify && cursor_notify_event->window == DefaultRootWindow(self->display)) {
            self->cursor_image_set = true;
            gsr_cursor_set_from_x11_cursor_image(self, XFixesGetCursorImage(self->display));
        }
    }

    if(!self->cursor_image_set) {
        self->cursor_image_set = true;
        gsr_cursor_set_from_x11_cursor_image(self, XFixesGetCursorImage(self->display));
    }
}

void gsr_cursor_tick(gsr_cursor *self, Window relative_to) {
    /* TODO: Use XInput2 instead. However that doesn't work when the pointer is grabbed. Maybe check for focused window change and XSelectInput PointerMask */
    Window dummy_window;
    int dummy_i;
    unsigned int dummy_u;
    XQueryPointer(self->display, relative_to, &dummy_window, &dummy_window, &dummy_i, &dummy_i, &self->position.x, &self->position.y, &dummy_u);
}