1/** 2 * Display/snoop the z/stencil/back/front buffers of another app's window. 3 * Also, an example of the need for shared ancillary renderbuffers. 4 * 5 * Hint: use 'xwininfo' to get a window's ID. 6 * 7 * Brian Paul 8 * 11 Oct 2007 9 */ 10 11#define GL_GLEXT_PROTOTYPES 12 13#include <GL/gl.h> 14#include <GL/glx.h> 15#include <stdio.h> 16#include <stdlib.h> 17#include <string.h> 18#include <X11/keysym.h> 19 20 21#define Z_BUFFER 1 22#define STENCIL_BUFFER 2 23#define BACK_BUFFER 3 24#define FRONT_BUFFER 4 25 26 27static int Buffer = BACK_BUFFER; 28static int WindowID = 0; 29static const char *DisplayName = NULL; 30static GLXContext Context = 0; 31static int Width, Height; 32 33 34/** 35 * Grab the z/stencil/back/front image from the srcWin and display it 36 * (possibly converted to grayscale) in the dstWin. 37 */ 38static void 39redraw(Display *dpy, Window srcWin, Window dstWin ) 40{ 41 GLubyte *image = malloc(Width * Height * 4); 42 43 glXMakeCurrent(dpy, srcWin, Context); 44 glPixelStorei(GL_PACK_ALIGNMENT, 1); 45 if (Buffer == BACK_BUFFER) { 46 glReadBuffer(GL_BACK); 47 glReadPixels(0, 0, Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, image); 48 } 49 else if (Buffer == FRONT_BUFFER) { 50 glReadBuffer(GL_FRONT); 51 glReadPixels(0, 0, Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, image); 52 } 53 else if (Buffer == Z_BUFFER) { 54 GLfloat *z = malloc(Width * Height * sizeof(GLfloat)); 55 int i; 56 glReadPixels(0, 0, Width, Height, GL_DEPTH_COMPONENT, GL_FLOAT, z); 57 for (i = 0; i < Width * Height; i++) { 58 image[i*4+0] = 59 image[i*4+1] = 60 image[i*4+2] = (GLint) (255.0 * z[i]); 61 image[i*4+3] = 255; 62 } 63 free(z); 64 } 65 else if (Buffer == STENCIL_BUFFER) { 66 GLubyte *sten = malloc(Width * Height * sizeof(GLubyte)); 67 int i, min = 100, max = -1; 68 float step; 69 int sz; 70 glGetIntegerv(GL_STENCIL_BITS, &sz); 71 glReadPixels(0, 0, Width, Height, 72 GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, sten); 73 /* find min/max for converting stencil to grayscale */ 74 for (i = 0; i < Width * Height; i++) { 75 if (sten[i] < min) 76 min = sten[i]; 77 if (sten[i] > max) 78 max = sten[i]; 79 } 80 if (min == max) 81 step = 0; 82 else 83 step = 255.0 / (float) (max - min); 84 for (i = 0; i < Width * Height; i++) { 85 image[i*4+0] = 86 image[i*4+1] = 87 image[i*4+2] = (GLint) ((sten[i] - min) * step); 88 image[i*4+3] = 255; 89 } 90 free(sten); 91 } 92 93 glXMakeCurrent(dpy, dstWin, Context); 94 glWindowPos2iARB(0, 0); 95 glDrawBuffer(GL_FRONT); 96 glDrawPixels(Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, image); 97 glFlush(); 98 99 free(image); 100} 101 102 103static void 104set_window_title(Display *dpy, Window win, const char *title) 105{ 106 XSizeHints sizehints; 107 sizehints.flags = 0; 108 XSetStandardProperties(dpy, win, title, title, 109 None, (char **)NULL, 0, &sizehints); 110} 111 112 113static Window 114make_gl_window(Display *dpy, XVisualInfo *visinfo, int width, int height) 115{ 116 int scrnum; 117 XSetWindowAttributes attr; 118 unsigned long mask; 119 Window root; 120 Window win; 121 int x = 0, y = 0; 122 char *name = NULL; 123 124 scrnum = DefaultScreen( dpy ); 125 root = RootWindow( dpy, scrnum ); 126 127 /* window attributes */ 128 attr.background_pixel = 0; 129 attr.border_pixel = 0; 130 attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone); 131 attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask; 132 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; 133 134 win = XCreateWindow( dpy, root, x, y, width, height, 135 0, visinfo->depth, InputOutput, 136 visinfo->visual, mask, &attr ); 137 138 /* set hints and properties */ 139 { 140 XSizeHints sizehints; 141 sizehints.x = x; 142 sizehints.y = y; 143 sizehints.width = width; 144 sizehints.height = height; 145 sizehints.flags = USSize | USPosition; 146 XSetNormalHints(dpy, win, &sizehints); 147 XSetStandardProperties(dpy, win, name, name, 148 None, (char **)NULL, 0, &sizehints); 149 } 150 151 return win; 152} 153 154 155static void 156update_window_title(Display *dpy, Window win) 157{ 158 char title[1000], *buf; 159 160 switch (Buffer) { 161 case Z_BUFFER: 162 buf = "Z"; 163 break; 164 case STENCIL_BUFFER: 165 buf = "Stencil"; 166 break; 167 case BACK_BUFFER: 168 buf = "Back"; 169 break; 170 case FRONT_BUFFER: 171 buf = "Front"; 172 break; 173 default: 174 buf = ""; 175 } 176 177 sprintf(title, "glxsnoop window 0x%x (%s buffer)", (int) WindowID, buf); 178 179 set_window_title(dpy, win, title); 180} 181 182 183static void 184keypress(Display *dpy, Window win, char key) 185{ 186 switch (key) { 187 case 27: 188 /* escape */ 189 exit(0); 190 break; 191 case 's': 192 Buffer = STENCIL_BUFFER; 193 break; 194 case 'z': 195 Buffer = Z_BUFFER; 196 break; 197 case 'f': 198 Buffer = FRONT_BUFFER; 199 break; 200 case 'b': 201 Buffer = BACK_BUFFER; 202 break; 203 default: 204 return; 205 } 206 207 update_window_title(dpy, win); 208 redraw(dpy, WindowID, win); 209} 210 211 212static void 213event_loop(Display *dpy, Window win) 214{ 215 XEvent event; 216 217 while (1) { 218 XNextEvent( dpy, &event ); 219 220 switch (event.type) { 221 case Expose: 222 redraw(dpy, WindowID, win); 223 break; 224 case ConfigureNotify: 225 /*resize( event.xconfigure.width, event.xconfigure.height );*/ 226 break; 227 case KeyPress: 228 { 229 char buffer[10]; 230 int code; 231 code = XLookupKeysym(&event.xkey, 0); 232 if (code == XK_Left) { 233 } 234 else { 235 XLookupString(&event.xkey, buffer, sizeof(buffer), 236 NULL, NULL); 237 keypress(dpy, win, buffer[0]); 238 } 239 } 240 default: 241 /* nothing */ 242 ; 243 } 244 } 245} 246 247 248static VisualID 249get_window_visualid(Display *dpy, Window win) 250{ 251 XWindowAttributes attr; 252 253 if (XGetWindowAttributes(dpy, win, &attr)) { 254 return attr.visual->visualid; 255 } 256 else { 257 return 0; 258 } 259} 260 261 262static void 263get_window_size(Display *dpy, Window win, int *w, int *h) 264{ 265 XWindowAttributes attr; 266 267 if (XGetWindowAttributes(dpy, win, &attr)) { 268 *w = attr.width; 269 *h = attr.height; 270 } 271 else { 272 *w = *h = 0; 273 } 274} 275 276 277static XVisualInfo * 278visualid_to_visualinfo(Display *dpy, VisualID vid) 279{ 280 XVisualInfo *vinfo, templ; 281 long mask; 282 int n; 283 284 templ.visualid = vid; 285 mask = VisualIDMask; 286 287 vinfo = XGetVisualInfo(dpy, mask, &templ, &n); 288 return vinfo; 289} 290 291 292static void 293key_usage(void) 294{ 295 printf("Keyboard:\n"); 296 printf(" z - display Z buffer\n"); 297 printf(" s - display stencil buffer\n"); 298 printf(" f - display front color buffer\n"); 299 printf(" b - display back buffer\n"); 300} 301 302 303static void 304usage(void) 305{ 306 printf("Usage: glxsnoop [-display dpy] windowID\n"); 307 key_usage(); 308} 309 310 311static void 312parse_opts(int argc, char *argv[]) 313{ 314 int i; 315 316 for (i = 1; i < argc; i++) { 317 if (strcmp(argv[i], "-h") == 0) { 318 usage(); 319 exit(0); 320 } 321 else if (strcmp(argv[i], "-display") == 0) { 322 DisplayName = argv[i + 1]; 323 i++; 324 } 325 else { 326 if (argv[i][0] == '0' && argv[i][1] == 'x') { 327 /* hex */ 328 WindowID = strtol(argv[i], NULL, 16); 329 } 330 else { 331 WindowID = atoi(argv[i]); 332 } 333 break; 334 } 335 } 336 337 if (!WindowID) { 338 usage(); 339 exit(0); 340 } 341} 342 343 344int 345main( int argc, char *argv[] ) 346{ 347 Display *dpy; 348 VisualID vid; 349 XVisualInfo *visinfo; 350 Window win; 351 352 parse_opts(argc, argv); 353 354 key_usage(); 355 356 dpy = XOpenDisplay(DisplayName); 357 358 /* find the VisualID for the named window */ 359 vid = get_window_visualid(dpy, WindowID); 360 get_window_size(dpy, WindowID, &Width, &Height); 361 362 visinfo = visualid_to_visualinfo(dpy, vid); 363 364 Context = glXCreateContext( dpy, visinfo, NULL, True ); 365 if (!Context) { 366 printf("Error: glXCreateContext failed\n"); 367 exit(1); 368 } 369 370 win = make_gl_window(dpy, visinfo, Width, Height); 371 XMapWindow(dpy, win); 372 update_window_title(dpy, win); 373 374 event_loop( dpy, win ); 375 376 return 0; 377} 378