diff --git a/Makefile b/Makefile index 8facbf6..1526bbb 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ ifeq ($(INSTALL),osx) SHADER_DIR = Library/glava endif -LDFLAGS = $(ASAN) -lpulse -lpulse-simple -pthread -lglfw -ldl -lm -lX11 -lXext +LDFLAGS = $(ASAN) -lpulse -lpulse-simple -pthread -lglfw -ldl -lm -lX11 -lXext -lXcomposite PYTHON = python diff --git a/glava.c b/glava.c index c67391d..83a371d 100644 --- a/glava.c +++ b/glava.c @@ -274,7 +274,7 @@ int main(int argc, char** argv) { pthread_mutex_unlock(&audio.mutex); /* Only render if needed (ie. stop rendering when fullscreen windows are focused) */ - if (xwin_should_render()) { + if (xwin_should_render(r)) { rd_update(r, lb, rb, r->bufsize_request, modified); } else { /* Sleep for 50ms and then attempt to render again */ diff --git a/render.c b/render.c index 2556416..370aba4 100644 --- a/render.c +++ b/render.c @@ -16,6 +16,18 @@ #include #include +#include +#include + +#define GLFW_EXPOSE_NATIVE_X11 + +/* Hack to make GLFW 3.1 headers work with GLava. We don't use the context APIs from GLFW, but + the old headers require one of them to be selected for exposure in glfw3native.h. */ +#if GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR <= 1 +#define GLFW_EXPOSE_NATIVE_GLX +#endif +#include + /* Fixes for old GLFW versions */ #ifndef GLFW_TRUE #define GLFW_TRUE GL_TRUE @@ -98,6 +110,7 @@ struct overlay_data { struct gl_data { struct gl_sfbo* stages; struct overlay_data overlay; + Display* display; GLuint audio_tex_r, audio_tex_l, bg_tex, sm_prog; size_t stages_sz, bufscale, avg_frames; GLFWwindow* w; @@ -117,6 +130,8 @@ struct gl_data { int geometry[4]; }; + + /* load shader file */ static GLuint shaderload(const char* rpath, GLenum type, @@ -732,6 +747,8 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force if (!glfwInit()) abort(); + gl->display = glfwGetX11Display(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_FLOATING, GLFW_FALSE); @@ -1576,6 +1593,10 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi } void* rd_get_impl_window(struct renderer* r) { return r->gl->w; } +Display* rd_get_x11_display(struct renderer* r) { return r->gl->display; } +Window rd_get_x11_window(struct renderer* r) { return glfwGetX11Window(r->gl->w); } +void rd_get_fbsize(struct renderer* r, int* w, int* h) { glfwGetFramebufferSize(r->gl->w, w, h); } +void rd_get_wpos(struct renderer* r, int* x, int* y) { glfwGetWindowPos(r->gl->w, x, y); } void rd_destroy(struct renderer* r) { /* TODO: delete everything else, not really needed though (as the application exits after here) */ diff --git a/render.h b/render.h index 1bdf36c..58af947 100644 --- a/render.h +++ b/render.h @@ -8,8 +8,16 @@ typedef struct renderer { struct gl_data* gl; } renderer; -struct renderer* rd_new(const char** paths, const char* entry, const char* force_mod); -void rd_update(struct renderer*, float* lb, float* rb, size_t bsz, bool modified); -void rd_destroy(struct renderer*); -void rd_time(struct renderer*); +struct renderer* rd_new (const char** paths, const char* entry, + const char* force_mod); +void rd_update (struct renderer*, float* lb, float* rb, + size_t bsz, bool modified); +void rd_destroy (struct renderer*); +void rd_time (struct renderer*); void* rd_get_impl_window(struct renderer*); +void rd_get_fbsize (struct renderer*, int* w, int* h); +void rd_get_wpos (struct renderer*, int* x, int* y); +#ifdef GLAVA_RDX11 +Display* rd_get_x11_display(struct renderer*); +Window rd_get_x11_window (struct renderer*); +#endif diff --git a/shaders/rc.glsl b/shaders/rc.glsl index f972db4..15a0fb5 100644 --- a/shaders/rc.glsl +++ b/shaders/rc.glsl @@ -17,7 +17,7 @@ /* GLFW window hints */ #request setfloating false -#request setdecorated false +#request setdecorated true #request setfocused false #request setmaximized false diff --git a/xwin.c b/xwin.c index e024fd6..78be91a 100644 --- a/xwin.c +++ b/xwin.c @@ -12,30 +12,21 @@ #include #include +#include #include +#include #include -#define GLFW_EXPOSE_NATIVE_X11 - #include -#include - -/* Hack to make GLFW 3.1 headers work with GLava. We don't use the context APIs from GLFW, but - the old headers require one of them to be selected for exposure in glfw3native.h. */ -#if GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR <= 1 -#define GLFW_EXPOSE_NATIVE_GLX -#endif -#include +#define GLAVA_RDX11 #include "render.h" -#include "xwin.h" - -static Window find_desktop(void) { +static Window find_desktop(struct renderer* r) { static Window desktop; static bool searched = false; if (!searched) { - Display* d = glfwGetX11Display(); + Display* d = rd_get_x11_display(r); desktop = DefaultRootWindow(d); Window _ignored, * children; unsigned int nret; @@ -47,7 +38,8 @@ static Window find_desktop(void) { if (name) { /* Mutter-based window managers */ if (!strcmp(name, "mutter guard window")) { - desktop = children[t]; + printf("Using mutter guard window instead of root window\n"); + // desktop = children[t]; t = nret; /* break after */ } XFree(name); @@ -60,9 +52,9 @@ static Window find_desktop(void) { return desktop; } -bool xwin_should_render(void) { +bool xwin_should_render(struct renderer* rd) { bool ret = true, should_close = false; - Display* d = glfwGetX11Display(); + Display* d = rd_get_x11_display(rd); if (!d) { d = XOpenDisplay(0); should_close = true; @@ -107,8 +99,8 @@ bool xwin_should_render(void) { /* Set window types defined by the EWMH standard, possible values: -> "desktop", "dock", "toolbar", "menu", "utility", "splash", "dialog", "normal" */ static void xwin_changeatom(struct renderer* rd, const char* type, const char* atom, const char* fmt, int mode) { - Window w = glfwGetX11Window((GLFWwindow*) rd_get_impl_window(rd)); - Display* d = glfwGetX11Display(); + Window w = rd_get_x11_window(rd); + Display* d = rd_get_x11_display(rd); Atom wtype = XInternAtom(d, atom, false); size_t len = strlen(type), t; char formatted[len + 1]; @@ -133,8 +125,8 @@ void xwin_addstate(struct renderer* rd, const char* state) { xwin_changeatom(rd, state, "_NET_WM_STATE", "_NET_WM_STATE_%s", PropModeAppend); } -static Pixmap get_pixmap(Display* d, Window w) { - Pixmap p; +static Drawable get_drawable(Display* d, Window w) { + Drawable p; Atom act_type; int act_format; unsigned long nitems, bytes_after; @@ -142,14 +134,14 @@ static Pixmap get_pixmap(Display* d, Window w) { Atom id; id = XInternAtom(d, "_XROOTPMAP_ID", False); - + if (XGetWindowProperty(d, w, id, 0, 1, False, XA_PIXMAP, &act_type, &act_format, &nitems, &bytes_after, - &data) == Success) { - if (data) { - p = *((Pixmap *) data); - XFree(data); - } + &data) == Success && data) { + p = *((Pixmap *) data); + XFree(data); + } else { + p = w; } return p; @@ -163,113 +155,123 @@ unsigned int xwin_copyglbg(struct renderer* rd, unsigned int tex) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - GLFWwindow* gwin = (GLFWwindow*) rd_get_impl_window(rd); + bool use_shm = true; + int x, y, w, h; - glfwGetFramebufferSize(gwin, &w, &h); - glfwGetWindowPos(gwin, &x, &y); + rd_get_fbsize(rd, &w, &h); + rd_get_wpos(rd, &x, &y); XColor c; - Display* d = glfwGetX11Display(); - Pixmap p = get_pixmap(d, find_desktop()); + Display* d = rd_get_x11_display(rd); + Drawable src = get_drawable(d, find_desktop(rd)); - /* Obtain section of root pixmap using XShm */ + /* Obtain section of root pixmap */ XShmSegmentInfo shminfo; Visual* visual = DefaultVisual(d, DefaultScreen(d)); XVisualInfo match = { .visualid = XVisualIDFromVisual(visual) }; int nret; XVisualInfo* info = XGetVisualInfo(d, VisualIDMask, &match, &nret); - XImage* image = XShmCreateImage(d, visual, info->depth, ZPixmap, NULL, - &shminfo, (unsigned int) w, (unsigned int) h); - if ((shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line * image->height, - IPC_CREAT | 0777)) == -1) { - fprintf(stderr, "shmget() failed: %s\n", strerror(errno)); - exit(EXIT_FAILURE); + XImage* image; + if (use_shm) { + image = XShmCreateImage(d, visual, info->depth, ZPixmap, NULL, + &shminfo, (unsigned int) w, (unsigned int) h); + if ((shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line * image->height, + IPC_CREAT | 0777)) == -1) { + fprintf(stderr, "shmget() failed: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + shminfo.shmaddr = image->data = shmat(shminfo.shmid, 0, 0); + shminfo.readOnly = false; + XShmAttach(d, &shminfo); + XShmGetImage(d, src, image, x, y, AllPlanes); + } else { + image = XGetImage(d, src, x, y, (unsigned int) w, (unsigned int) h, + ZPixmap, AllPlanes); } - shminfo.shmaddr = image->data = shmat(shminfo.shmid, 0, 0); - shminfo.readOnly = false; - XShmAttach(d, &shminfo); - XShmGetImage(d, p, image, x, y, AllPlanes); /* Try to convert pixel bit depth to OpenGL storage format. The following formats\ will need intermediate conversion before OpenGL can accept the data: - 8-bit pixel formats (retro displays, low-bandwidth virtual displays) - 36-bit pixel formats (rare deep color displays) */ - - bool invalid = false, aligned = false; - GLenum type; - switch (image->bits_per_pixel) { - case 16: - switch (image->depth) { - case 12: type = GL_UNSIGNED_SHORT_4_4_4_4; break; /* 12-bit (rare) */ - case 15: type = GL_UNSIGNED_SHORT_5_5_5_1; break; /* 15-bit, hi-color */ - case 16: /* 16-bit, hi-color */ - type = GL_UNSIGNED_SHORT_5_6_5; - aligned = true; - break; - } - break; - case 32: - switch (image->depth) { - case 24: type = GL_UNSIGNED_BYTE; break; /* 24-bit, true color */ - case 30: type = GL_UNSIGNED_INT_10_10_10_2; break; /* 30-bit, deep color */ - } - break; - case 64: - if (image->depth == 48) /* 48-bit deep color */ - type = GL_UNSIGNED_SHORT; - else goto invalid; - break; - /* >64-bit formats */ - case 128: - if (image->depth == 96) - type = GL_UNSIGNED_INT; - else goto invalid; - break; - default: - invalid: invalid = true; - } - - uint8_t* buf; - if (invalid) { - abort(); - /* Manual reformat (slow) */ - buf = malloc(4 * w * h); - int xi, yi; - Colormap map = DefaultColormap(d, DefaultScreen(d)); - for (yi = 0; yi < h; ++yi) { - for (xi = 0; xi < w; ++xi) { - c.pixel = XGetPixel(image, xi, yi); - XQueryColor(d, map, &c); - size_t base = (xi + (yi * w)) * 4; - buf[base + 0] = c.red / 256; - buf[base + 1] = c.green / 256; - buf[base + 2] = c.blue / 256; - buf[base + 3] = 255; + + if (image) { + bool invalid = false, aligned = false; + GLenum type; + switch (image->bits_per_pixel) { + case 16: + switch (image->depth) { + case 12: type = GL_UNSIGNED_SHORT_4_4_4_4; break; /* 12-bit (rare) */ + case 15: type = GL_UNSIGNED_SHORT_5_5_5_1; break; /* 15-bit, hi-color */ + case 16: /* 16-bit, hi-color */ + type = GL_UNSIGNED_SHORT_5_6_5; + aligned = true; + break; } + break; + case 32: + switch (image->depth) { + case 24: type = GL_UNSIGNED_BYTE; break; /* 24-bit, true color */ + case 30: type = GL_UNSIGNED_INT_10_10_10_2; break; /* 30-bit, deep color */ + } + break; + case 64: + if (image->depth == 48) /* 48-bit deep color */ + type = GL_UNSIGNED_SHORT; + else goto invalid; + break; + /* >64-bit formats */ + case 128: + if (image->depth == 96) + type = GL_UNSIGNED_INT; + else goto invalid; + break; + default: + invalid: invalid = true; } - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); - free(buf); - } else { - /* Use image data directly. The alpha value is garbage/unassigned data, but - we need to read it because X11 keeps pixel data aligned */ - buf = (uint8_t*) image->data; - /* Data could be 2, 4, or 8 byte aligned, the RGBA format and type (depth) - already ensures reads will be properly aligned across scanlines */ - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - GLenum format = image->bitmap_bit_order == LSBFirst ? - (!aligned ? GL_BGRA : GL_BGR) : - (!aligned ? GL_RGBA : GL_RGB); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, format, type, buf); - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); /* restore default */ + uint8_t* buf; + if (invalid) { + abort(); + /* Manual reformat (slow) */ + buf = malloc(4 * w * h); + int xi, yi; + Colormap map = DefaultColormap(d, DefaultScreen(d)); + for (yi = 0; yi < h; ++yi) { + for (xi = 0; xi < w; ++xi) { + c.pixel = XGetPixel(image, xi, yi); + XQueryColor(d, map, &c); + size_t base = (xi + (yi * w)) * 4; + buf[base + 0] = c.red / 256; + buf[base + 1] = c.green / 256; + buf[base + 2] = c.blue / 256; + buf[base + 3] = 255; + } + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); + free(buf); + } else { + /* Use image data directly. The alpha value is garbage/unassigned data, but + we need to read it because X11 keeps pixel data aligned */ + buf = (uint8_t*) image->data; + /* Data could be 2, 4, or 8 byte aligned, the RGBA format and type (depth) + already ensures reads will be properly aligned across scanlines */ + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + GLenum format = image->bitmap_bit_order == LSBFirst ? + (!aligned ? GL_BGRA : GL_BGR) : + (!aligned ? GL_RGBA : GL_RGB); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, format, type, buf); + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); /* restore default */ + } + } + if (use_shm) { + XShmDetach(d, &shminfo); + shmdt(shminfo.shmaddr); + shmctl(shminfo.shmid, IPC_RMID, NULL); } - XShmDetach(d, &shminfo); - shmdt(shminfo.shmaddr); - shmctl(shminfo.shmid, IPC_RMID, NULL); - - XDestroyImage(image); + if (image) XDestroyImage(image); return texture; } diff --git a/xwin.h b/xwin.h index 16f2d43..755e8a0 100644 --- a/xwin.h +++ b/xwin.h @@ -1,5 +1,5 @@ -bool xwin_should_render(void); +bool xwin_should_render(struct renderer* rd); void xwin_settype(struct renderer* rd, const char* type); void xwin_addstate(struct renderer* rd, const char* state); unsigned int xwin_copyglbg(struct renderer* rd, unsigned int texture);