aboutsummaryrefslogtreecommitdiff
path: root/copy_pixels_swap_hack.c
blob: 2aff551918f7d37ca990342f55c9a1e9c9cc4e36 (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
// copy_pixels_swap_hack.c
//
// Call glXSwapBuffers instead of glCopyPixels if OpenGL read and draw buffers
// would perform a copy from back to front buffer.
//
// compile with
// gcc -shared -o copy_pixels_swap_hack.so -fPIC -O2 copy_pixels_swap.c -ldl
//
// use with
// LD_PRELOAD=copy_pixels_swap_hack.so ${YOUR_PROGRAM}
//
// Refer to the ld.so(8) manpage for the path at which the preloaded shared
// objects is searched for.
#define _GNU_SOURCE
#include <dlfcn.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <X11/Xlib.h>
void glCopyPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type)
{
	static int first = 1;
	static Display*    (*glX_GetCurrentDisplay)(void) = NULL;
	static GLXDrawable (*glX_GetCurrentDrawable)(void) = NULL;
	static Status (*X_GetGeometry)(Display*,Drawable,Window*,int*,int*,unsigned*,unsigned*,unsigned*,unsigned*);
	static void (*glX_SwapBuffers)(Display*,GLXDrawable) = NULL;
	static void (*gl_WindowPos3i)(GLint,GLint,GLint) = NULL;
	static void (*gl_CopyPixels)(GLint,GLint,GLsizei,GLsizei,GLenum) = NULL;
	static void (*gl_GetIntegerv)(GLenum,GLint*) = NULL;
	static void (*gl_DrawBuffer)(GLenum) = NULL;
	static void (*gl_ReadBuffer)(GLenum) = NULL;
	if( first ){
		*(void**)(&glX_GetCurrentDisplay)  = dlsym(RTLD_NEXT, "glXGetCurrentDisplay");
		*(void**)(&glX_GetCurrentDrawable) = dlsym(RTLD_NEXT, "glXGetCurrentDrawable");
		*(void**)(&glX_SwapBuffers)        = dlsym(RTLD_NEXT, "glXSwapBuffers");
		*(void**)(&X_GetGeometry)          = dlsym(RTLD_NEXT, "XGetGeometry");
		*(void**)(&gl_WindowPos3i)         = dlsym(RTLD_NEXT, "glWindowPos3i");
		*(void**)(&gl_CopyPixels)          = dlsym(RTLD_NEXT, "glCopyPixels");
		*(void**)(&gl_GetIntegerv)         = dlsym(RTLD_NEXT, "glGetIntegerv");
		*(void**)(&gl_DrawBuffer)          = dlsym(RTLD_NEXT, "glDrawBuffer");
		*(void**)(&gl_ReadBuffer)          = dlsym(RTLD_NEXT, "glReadBuffer");
		first = 0;
	}

	Display *dpy;
	GLXDrawable drw;
	if( !glX_GetCurrentDisplay || !glX_GetCurrentDrawable || !glX_SwapBuffers
	 || !gl_GetIntegerv || !gl_WindowPos3i || !gl_CopyPixels
	 || !gl_DrawBuffer || !gl_ReadBuffer
	 || !(dpy = glX_GetCurrentDisplay())
	 || !(drw = glX_GetCurrentDrawable())
	){
		return;
	}

	GLint buffer_draw =  0;
	GLint buffer_read = ~0;
	gl_GetIntegerv(GL_DRAW_BUFFER, &buffer_draw);
	gl_GetIntegerv(GL_READ_BUFFER, &buffer_read);
	if( GL_COLOR == type
	 && GL_FRONT == buffer_draw
	 && GL_BACK  == buffer_read
	){
		GLint rp[4] = {0,0,0,0};
		Window root;
		int geom_x, geom_y;
		unsigned geom_width, geom_height, border, depth;
		gl_GetIntegerv(GL_CURRENT_RASTER_POSITION, rp);
		X_GetGeometry(dpy, drw, &root, &geom_x, &geom_y, &geom_width, &geom_height, &border, &depth);

		glX_SwapBuffers(dpy, drw);
		gl_DrawBuffer(GL_BACK);
		gl_ReadBuffer(GL_FRONT);
		gl_WindowPos3i(0,0,0);
		gl_CopyPixels(0, 0, geom_width, geom_height, type);
		gl_DrawBuffer(GL_FRONT);
		gl_ReadBuffer(GL_BACK);
		gl_WindowPos3i(rp[0],rp[1],rp[2]);
	} else {
		gl_CopyPixels(x,y,width,height,type);
	}
}