From 49ac7d74292339fe9d52ce19de23d0ce25fa9bd1 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 21 Apr 2023 20:59:06 +0200 Subject: Add code to copy cursor image to opengl texture and update on event update (not used yet) --- README.md | 6 +-- build.sh | 2 +- include/cursor.h | 28 ++++++++++++ include/egl.h | 4 ++ project.conf | 1 + src/cursor.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/egl.c | 1 + 7 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 include/cursor.h create mode 100644 src/cursor.c diff --git a/README.md b/README.md index ce3e1ee..ad232ac 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,11 @@ to install GPU Screen Recorder on non-arch based distros. # Dependencies ## AMD -`libglvnd (which provides libgl and libegl), mesa, ffmpeg (libavcodec, libavformat, libavutil, libswresample, libavfilter), libx11, libxcomposite, libxrandr, libpulse, libva, libva-mesa-driver, libdrm, libcap, polkit (for pkexec)`. +`libglvnd (which provides libgl and libegl), mesa, ffmpeg (libavcodec, libavformat, libavutil, libswresample, libavfilter), libx11, libxcomposite, libxrandr, libxfixes, libpulse, libva, libva-mesa-driver, libdrm, libcap, polkit (for pkexec)`. ## Intel -`libglvnd (which provides libgl and libegl), mesa, ffmpeg (libavcodec, libavformat, libavutil, libswresample, libavfilter), libx11, libxcomposite, libxrandr, libpulse, libva, libva-intel-driver, libdrm, libcap, polkit (for pkexec)`. +`libglvnd (which provides libgl and libegl), mesa, ffmpeg (libavcodec, libavformat, libavutil, libswresample, libavfilter), libx11, libxcomposite, libxrandr, libxfixes, libpulse, libva, libva-intel-driver, libdrm, libcap, polkit (for pkexec)`. ## NVIDIA -`libglvnd (which provides libgl and libegl), ffmpeg (libavcodec, libavformat, libavutil, libswresample, libavfilter), libx11, libxcomposite, libxrandr, libpulse, cuda (libnvidia-compute), nvenc (libnvidia-encode), libva, libdrm, libcap`. Additionally, you need to have `nvfbc (libnvidia-fbc1)` installed when using nvfbc and `xnvctrl (libxnvctrl0)` when using the `-oc` option. +`libglvnd (which provides libgl and libegl), ffmpeg (libavcodec, libavformat, libavutil, libswresample, libavfilter), libx11, libxcomposite, libxrandr, libxfixes, libpulse, cuda (libnvidia-compute), nvenc (libnvidia-encode), libva, libdrm, libcap`. Additionally, you need to have `nvfbc (libnvidia-fbc1)` installed when using nvfbc and `xnvctrl (libxnvctrl0)` when using the `-oc` option. # How to use Run `scripts/interactive.sh` or run gpu-screen-recorder directly, for example: `gpu-screen-recorder -w $(xdotool selectwindow) -c mp4 -f 60 -a "$(pactl get-default-sink).monitor" -o test_video.mp4` then stop the screen recorder with Ctrl+C, which will also save the recording. You can change -w to -w screen if you want to record all monitors or if you want to record a specific monitor then you can use -w monitor-name, for example -w HDMI-0 (use xrandr command to find the name of your monitor. The name can also be found in your desktop environments display settings).\ diff --git a/build.sh b/build.sh index 22c713b..9fcc85e 100755 --- a/build.sh +++ b/build.sh @@ -10,7 +10,7 @@ build_gsr_kms_server() { } build_gsr() { - dependencies="libavcodec libavformat libavutil x11 xcomposite xrandr libpulse libswresample libavfilter libva libcap" + dependencies="libavcodec libavformat libavutil x11 xcomposite xrandr xfixes libpulse libswresample libavfilter libva libcap" includes="$(pkg-config --cflags $dependencies)" libs="$(pkg-config --libs $dependencies) -ldl -pthread -lm" opts="-O2 -g0 -DNDEBUG" diff --git a/include/cursor.h b/include/cursor.h new file mode 100644 index 0000000..0a7831d --- /dev/null +++ b/include/cursor.h @@ -0,0 +1,28 @@ +#ifndef GSR_CURSOR_H +#define GSR_CURSOR_H + +#include "egl.h" +#include "vec2.h" + +#include + +typedef struct { + gsr_egl *egl; + Display *display; + Window window; + int x_fixes_event_base; + + unsigned int texture_id; + vec2i size; + vec2i hotspot; + + bool cursor_image_set; +} gsr_cursor; + +int gsr_cursor_init(gsr_cursor *self, gsr_egl *egl, Display *display); +void gsr_cursor_deinit(gsr_cursor *self); + +int gsr_cursor_change_window_target(gsr_cursor *self, Window window); +void gsr_cursor_update(gsr_cursor *self, XEvent *xev); + +#endif /* GSR_CURSOR_H */ diff --git a/include/egl.h b/include/egl.h index 891c7a0..0d291d0 100644 --- a/include/egl.h +++ b/include/egl.h @@ -58,12 +58,15 @@ typedef void (*__eglMustCastToProperFunctionPointerType)(void); #define GL_TRIANGLES 0x0004 #define GL_TEXTURE_2D 0x0DE1 #define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +#define GL_RGBA8 0x8058 #define GL_UNSIGNED_BYTE 0x1401 #define GL_COLOR_BUFFER_BIT 0x00004000 #define GL_TEXTURE_WRAP_S 0x2802 #define GL_TEXTURE_WRAP_T 0x2803 #define GL_TEXTURE_MAG_FILTER 0x2800 #define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 #define GL_TEXTURE_WIDTH 0x1000 #define GL_TEXTURE_HEIGHT 0x1001 #define GL_NEAREST 0x2600 @@ -126,6 +129,7 @@ typedef struct { void (*glTexImage2D)(unsigned int target, int level, int internalFormat, int width, int height, int border, unsigned int format, unsigned int type, const void *pixels); void (*glCopyImageSubData)(unsigned int srcName, unsigned int srcTarget, int srcLevel, int srcX, int srcY, int srcZ, unsigned int dstName, unsigned int dstTarget, int dstLevel, int dstX, int dstY, int dstZ, int srcWidth, int srcHeight, int srcDepth); void (*glClearTexImage)(unsigned int texture, unsigned int level, unsigned int format, unsigned int type, const void *data); + void (*glGenerateMipmap)(unsigned int target); void (*glGenFramebuffers)(int n, unsigned int *framebuffers); void (*glBindFramebuffer)(unsigned int target, unsigned int framebuffer); void (*glDeleteFramebuffers)(int n, const unsigned int *framebuffers); diff --git a/project.conf b/project.conf index 4a5badb..ee1d00b 100644 --- a/project.conf +++ b/project.conf @@ -19,3 +19,4 @@ libswresample = ">=3" libavfilter = ">=5" libva = ">=1" libcap = ">=2" +xfixes = ">=2" \ No newline at end of file diff --git a/src/cursor.c b/src/cursor.c new file mode 100644 index 0000000..29a95d8 --- /dev/null +++ b/src/cursor.c @@ -0,0 +1,127 @@ +#include "../include/cursor.h" + +#include +#include +#include +#include + +#include + +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->glGenerateMipmap(GL_TEXTURE_2D); + + 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_MIPMAP_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) { + assert(egl); + assert(display); + memset(self, 0, sizeof(*self)); + self->egl = egl; + self->display = display; + + self->x_fixes_event_base = 0; + int x_fixes_error_base = 0; + if(!XFixesQueryExtension(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. no cursor will be visible in the video\n"); + gsr_cursor_deinit(self); + return -1; + } + + self->egl->glGenTextures(1, &self->texture_id); + 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; + } + + if(self->window) { + XFixesSelectCursorInput(self->display, self->window, 0); + self->window = None; + } + + self->display = NULL; + self->egl = NULL; +} + +int gsr_cursor_change_window_target(gsr_cursor *self, Window window) { + if(self->window) + XFixesSelectCursorInput(self->display, self->window, 0); + + XFixesSelectCursorInput(self->display, window, XFixesDisplayCursorNotifyMask); + self->window = window; + self->cursor_image_set = false; + return 0; +} + +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 == self->window) { + self->cursor_image_set = true; + gsr_cursor_set_from_x11_cursor_image(self, XFixesGetCursorImage(self->display)); + } + } + + if(!self->cursor_image_set && self->window) { + self->cursor_image_set = true; + gsr_cursor_set_from_x11_cursor_image(self, XFixesGetCursorImage(self->display)); + } +} diff --git a/src/egl.c b/src/egl.c index c5b14b9..7bf6e7f 100644 --- a/src/egl.c +++ b/src/egl.c @@ -138,6 +138,7 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) { { (void**)&self->glTexImage2D, "glTexImage2D" }, { (void**)&self->glCopyImageSubData, "glCopyImageSubData" }, { (void**)&self->glClearTexImage, "glClearTexImage" }, + { (void**)&self->glGenerateMipmap, "glGenerateMipmap" }, { (void**)&self->glGenFramebuffers, "glGenFramebuffers" }, { (void**)&self->glBindFramebuffer, "glBindFramebuffer" }, { (void**)&self->glDeleteFramebuffers, "glDeleteFramebuffers" }, -- cgit v1.2.3