378 lines
7.9 KiB
C
378 lines
7.9 KiB
C
/**
|
|
* Display/snoop the z/stencil/back/front buffers of another app's window.
|
|
* Also, an example of the need for shared ancillary renderbuffers.
|
|
*
|
|
* Hint: use 'xwininfo' to get a window's ID.
|
|
*
|
|
* Brian Paul
|
|
* 11 Oct 2007
|
|
*/
|
|
|
|
#define GL_GLEXT_PROTOTYPES
|
|
|
|
#include <GL/gl.h>
|
|
#include <GL/glx.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <X11/keysym.h>
|
|
|
|
|
|
#define Z_BUFFER 1
|
|
#define STENCIL_BUFFER 2
|
|
#define BACK_BUFFER 3
|
|
#define FRONT_BUFFER 4
|
|
|
|
|
|
static int Buffer = BACK_BUFFER;
|
|
static int WindowID = 0;
|
|
static const char *DisplayName = NULL;
|
|
static GLXContext Context = 0;
|
|
static int Width, Height;
|
|
|
|
|
|
/**
|
|
* Grab the z/stencil/back/front image from the srcWin and display it
|
|
* (possibly converted to grayscale) in the dstWin.
|
|
*/
|
|
static void
|
|
redraw(Display *dpy, Window srcWin, Window dstWin )
|
|
{
|
|
GLubyte *image = malloc(Width * Height * 4);
|
|
|
|
glXMakeCurrent(dpy, srcWin, Context);
|
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
|
if (Buffer == BACK_BUFFER) {
|
|
glReadBuffer(GL_BACK);
|
|
glReadPixels(0, 0, Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, image);
|
|
}
|
|
else if (Buffer == FRONT_BUFFER) {
|
|
glReadBuffer(GL_FRONT);
|
|
glReadPixels(0, 0, Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, image);
|
|
}
|
|
else if (Buffer == Z_BUFFER) {
|
|
GLfloat *z = malloc(Width * Height * sizeof(GLfloat));
|
|
int i;
|
|
glReadPixels(0, 0, Width, Height, GL_DEPTH_COMPONENT, GL_FLOAT, z);
|
|
for (i = 0; i < Width * Height; i++) {
|
|
image[i*4+0] =
|
|
image[i*4+1] =
|
|
image[i*4+2] = (GLint) (255.0 * z[i]);
|
|
image[i*4+3] = 255;
|
|
}
|
|
free(z);
|
|
}
|
|
else if (Buffer == STENCIL_BUFFER) {
|
|
GLubyte *sten = malloc(Width * Height * sizeof(GLubyte));
|
|
int i, min = 100, max = -1;
|
|
float step;
|
|
int sz;
|
|
glGetIntegerv(GL_STENCIL_BITS, &sz);
|
|
glReadPixels(0, 0, Width, Height,
|
|
GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, sten);
|
|
/* find min/max for converting stencil to grayscale */
|
|
for (i = 0; i < Width * Height; i++) {
|
|
if (sten[i] < min)
|
|
min = sten[i];
|
|
if (sten[i] > max)
|
|
max = sten[i];
|
|
}
|
|
if (min == max)
|
|
step = 0;
|
|
else
|
|
step = 255.0 / (float) (max - min);
|
|
for (i = 0; i < Width * Height; i++) {
|
|
image[i*4+0] =
|
|
image[i*4+1] =
|
|
image[i*4+2] = (GLint) ((sten[i] - min) * step);
|
|
image[i*4+3] = 255;
|
|
}
|
|
free(sten);
|
|
}
|
|
|
|
glXMakeCurrent(dpy, dstWin, Context);
|
|
glWindowPos2iARB(0, 0);
|
|
glDrawBuffer(GL_FRONT);
|
|
glDrawPixels(Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, image);
|
|
glFlush();
|
|
|
|
free(image);
|
|
}
|
|
|
|
|
|
static void
|
|
set_window_title(Display *dpy, Window win, const char *title)
|
|
{
|
|
XSizeHints sizehints;
|
|
sizehints.flags = 0;
|
|
XSetStandardProperties(dpy, win, title, title,
|
|
None, (char **)NULL, 0, &sizehints);
|
|
}
|
|
|
|
|
|
static Window
|
|
make_gl_window(Display *dpy, XVisualInfo *visinfo, int width, int height)
|
|
{
|
|
int scrnum;
|
|
XSetWindowAttributes attr;
|
|
unsigned long mask;
|
|
Window root;
|
|
Window win;
|
|
int x = 0, y = 0;
|
|
char *name = NULL;
|
|
|
|
scrnum = DefaultScreen( dpy );
|
|
root = RootWindow( dpy, scrnum );
|
|
|
|
/* window attributes */
|
|
attr.background_pixel = 0;
|
|
attr.border_pixel = 0;
|
|
attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone);
|
|
attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
|
|
mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
|
|
|
|
win = XCreateWindow( dpy, root, x, y, width, height,
|
|
0, visinfo->depth, InputOutput,
|
|
visinfo->visual, mask, &attr );
|
|
|
|
/* set hints and properties */
|
|
{
|
|
XSizeHints sizehints;
|
|
sizehints.x = x;
|
|
sizehints.y = y;
|
|
sizehints.width = width;
|
|
sizehints.height = height;
|
|
sizehints.flags = USSize | USPosition;
|
|
XSetNormalHints(dpy, win, &sizehints);
|
|
XSetStandardProperties(dpy, win, name, name,
|
|
None, (char **)NULL, 0, &sizehints);
|
|
}
|
|
|
|
return win;
|
|
}
|
|
|
|
|
|
static void
|
|
update_window_title(Display *dpy, Window win)
|
|
{
|
|
char title[1000], *buf;
|
|
|
|
switch (Buffer) {
|
|
case Z_BUFFER:
|
|
buf = "Z";
|
|
break;
|
|
case STENCIL_BUFFER:
|
|
buf = "Stencil";
|
|
break;
|
|
case BACK_BUFFER:
|
|
buf = "Back";
|
|
break;
|
|
case FRONT_BUFFER:
|
|
buf = "Front";
|
|
break;
|
|
default:
|
|
buf = "";
|
|
}
|
|
|
|
sprintf(title, "glxsnoop window 0x%x (%s buffer)", (int) WindowID, buf);
|
|
|
|
set_window_title(dpy, win, title);
|
|
}
|
|
|
|
|
|
static void
|
|
keypress(Display *dpy, Window win, char key)
|
|
{
|
|
switch (key) {
|
|
case 27:
|
|
/* escape */
|
|
exit(0);
|
|
break;
|
|
case 's':
|
|
Buffer = STENCIL_BUFFER;
|
|
break;
|
|
case 'z':
|
|
Buffer = Z_BUFFER;
|
|
break;
|
|
case 'f':
|
|
Buffer = FRONT_BUFFER;
|
|
break;
|
|
case 'b':
|
|
Buffer = BACK_BUFFER;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
update_window_title(dpy, win);
|
|
redraw(dpy, WindowID, win);
|
|
}
|
|
|
|
|
|
static void
|
|
event_loop(Display *dpy, Window win)
|
|
{
|
|
XEvent event;
|
|
|
|
while (1) {
|
|
XNextEvent( dpy, &event );
|
|
|
|
switch (event.type) {
|
|
case Expose:
|
|
redraw(dpy, WindowID, win);
|
|
break;
|
|
case ConfigureNotify:
|
|
/*resize( event.xconfigure.width, event.xconfigure.height );*/
|
|
break;
|
|
case KeyPress:
|
|
{
|
|
char buffer[10];
|
|
int r, code;
|
|
code = XLookupKeysym(&event.xkey, 0);
|
|
if (code == XK_Left) {
|
|
}
|
|
else {
|
|
r = XLookupString(&event.xkey, buffer, sizeof(buffer),
|
|
NULL, NULL);
|
|
keypress(dpy, win, buffer[0]);
|
|
}
|
|
}
|
|
default:
|
|
/* nothing */
|
|
;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static VisualID
|
|
get_window_visualid(Display *dpy, Window win)
|
|
{
|
|
XWindowAttributes attr;
|
|
|
|
if (XGetWindowAttributes(dpy, win, &attr)) {
|
|
return attr.visual->visualid;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
get_window_size(Display *dpy, Window win, int *w, int *h)
|
|
{
|
|
XWindowAttributes attr;
|
|
|
|
if (XGetWindowAttributes(dpy, win, &attr)) {
|
|
*w = attr.width;
|
|
*h = attr.height;
|
|
}
|
|
else {
|
|
*w = *h = 0;
|
|
}
|
|
}
|
|
|
|
|
|
static XVisualInfo *
|
|
visualid_to_visualinfo(Display *dpy, VisualID vid)
|
|
{
|
|
XVisualInfo *vinfo, templ;
|
|
long mask;
|
|
int n;
|
|
|
|
templ.visualid = vid;
|
|
mask = VisualIDMask;
|
|
|
|
vinfo = XGetVisualInfo(dpy, mask, &templ, &n);
|
|
return vinfo;
|
|
}
|
|
|
|
|
|
static void
|
|
key_usage(void)
|
|
{
|
|
printf("Keyboard:\n");
|
|
printf(" z - display Z buffer\n");
|
|
printf(" s - display stencil buffer\n");
|
|
printf(" f - display front color buffer\n");
|
|
printf(" b - display back buffer\n");
|
|
}
|
|
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
printf("Usage: glxsnoop [-display dpy] windowID\n");
|
|
key_usage();
|
|
}
|
|
|
|
|
|
static void
|
|
parse_opts(int argc, char *argv[])
|
|
{
|
|
int i;
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
if (strcmp(argv[i], "-h") == 0) {
|
|
usage();
|
|
exit(0);
|
|
}
|
|
else if (strcmp(argv[i], "-display") == 0) {
|
|
DisplayName = argv[i + 1];
|
|
i++;
|
|
}
|
|
else {
|
|
if (argv[i][0] == '0' && argv[i][1] == 'x') {
|
|
/* hex */
|
|
WindowID = strtol(argv[i], NULL, 16);
|
|
}
|
|
else {
|
|
WindowID = atoi(argv[i]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!WindowID) {
|
|
usage();
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
main( int argc, char *argv[] )
|
|
{
|
|
Display *dpy;
|
|
VisualID vid;
|
|
XVisualInfo *visinfo;
|
|
Window win;
|
|
|
|
parse_opts(argc, argv);
|
|
|
|
key_usage();
|
|
|
|
dpy = XOpenDisplay(DisplayName);
|
|
|
|
/* find the VisualID for the named window */
|
|
vid = get_window_visualid(dpy, WindowID);
|
|
get_window_size(dpy, WindowID, &Width, &Height);
|
|
|
|
visinfo = visualid_to_visualinfo(dpy, vid);
|
|
|
|
Context = glXCreateContext( dpy, visinfo, NULL, True );
|
|
if (!Context) {
|
|
printf("Error: glXCreateContext failed\n");
|
|
exit(1);
|
|
}
|
|
|
|
win = make_gl_window(dpy, visinfo, Width, Height);
|
|
XMapWindow(dpy, win);
|
|
update_window_title(dpy, win);
|
|
|
|
event_loop( dpy, win );
|
|
|
|
return 0;
|
|
}
|