diff options
author | dec05eba <dec05eba@protonmail.com> | 2024-03-09 16:59:09 +0100 |
---|---|---|
committer | dec05eba <dec05eba@protonmail.com> | 2024-03-09 16:59:09 +0100 |
commit | a3fedae32937f8965c9905e12633fa4de0c3658e (patch) | |
tree | a8596397292e6fc674f8fe3c8f09f17b63b0d646 /src/cursor.c | |
parent | 5e05bbbbcbd45298c48af2b56a33da93d15b8f44 (diff) |
Draw cursor in window capture
Diffstat (limited to 'src/cursor.c')
-rw-r--r-- | src/cursor.c | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/src/cursor.c b/src/cursor.c new file mode 100644 index 0000000..737c33b --- /dev/null +++ b/src/cursor.c @@ -0,0 +1,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); +} |