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