manywin.c revision 32001f49
1/*
2 * Create N GLX windows/contexts and render to them in round-robin order.
3 * Also, have the contexts share all texture objects.
4 * Press 'd' to delete a texture, 'u' to unbind it.
5 *
6 * Copyright (C) 2000  Brian Paul   All Rights Reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included
16 * in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
21 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26
27#include <GL/gl.h>
28#include <GL/glx.h>
29#include <assert.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <unistd.h>
34#include <X11/keysym.h>
35
36
37/*
38 * Each display/window/context:
39 */
40struct head {
41   char DisplayName[1000];
42   Display *Dpy;
43   Window Win;
44   GLXContext Context;
45   float Angle;
46   char Renderer[1000];
47   char Vendor[1000];
48   char Version[1000];
49};
50
51
52#define MAX_HEADS 200
53static struct head Heads[MAX_HEADS];
54static int NumHeads = 0;
55static GLboolean SwapSeparate = GL_TRUE;
56static GLuint TexObj = 0;
57
58
59static void
60Error(const char *display, const char *msg)
61{
62   fprintf(stderr, "Error on display %s - %s\n", XDisplayName(display), msg);
63   exit(1);
64}
65
66
67static struct head *
68AddHead(const char *displayName, const char *name)
69{
70   Display *dpy;
71   Window win;
72   GLXContext ctx;
73   int attrib[] = { GLX_RGBA,
74		    GLX_RED_SIZE, 1,
75		    GLX_GREEN_SIZE, 1,
76		    GLX_BLUE_SIZE, 1,
77		    GLX_DOUBLEBUFFER,
78		    None };
79   int scrnum;
80   XSetWindowAttributes attr;
81   unsigned long mask;
82   Window root;
83   XVisualInfo *visinfo;
84   int width = 90, height = 90;
85   int xpos = 0, ypos = 0;
86
87   if (NumHeads >= MAX_HEADS)
88      return NULL;
89
90   dpy = XOpenDisplay(displayName);
91   if (!dpy) {
92      Error(displayName, "Unable to open display");
93      return NULL;
94   }
95
96   scrnum = DefaultScreen(dpy);
97   root = RootWindow(dpy, scrnum);
98
99   visinfo = glXChooseVisual(dpy, scrnum, attrib);
100   if (!visinfo) {
101      Error(displayName, "Unable to find RGB, double-buffered visual");
102      return NULL;
103   }
104
105   /* window attributes */
106   xpos = (NumHeads % 10) * 100;
107   ypos = (NumHeads / 10) * 100;
108   printf("%d, %d\n", xpos, ypos);
109   attr.background_pixel = 0;
110   attr.border_pixel = 0;
111   attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone);
112   attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
113   mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
114
115   win = XCreateWindow(dpy, root, xpos, ypos, width, height,
116		        0, visinfo->depth, InputOutput,
117		        visinfo->visual, mask, &attr);
118   if (!win) {
119      Error(displayName, "Couldn't create window");
120      return NULL;
121   }
122
123   {
124      XSizeHints sizehints;
125      sizehints.x = xpos;
126      sizehints.y = ypos;
127      sizehints.width  = width;
128      sizehints.height = height;
129      sizehints.flags = USSize | USPosition;
130      XSetNormalHints(dpy, win, &sizehints);
131      XSetStandardProperties(dpy, win, name, name,
132                              None, (char **)NULL, 0, &sizehints);
133   }
134
135   if (NumHeads == 0) {
136      ctx = glXCreateContext(dpy, visinfo, NULL, True);
137   }
138   else {
139      /* share textures & dlists with 0th context */
140      printf("sharing\n");
141      ctx = glXCreateContext(dpy, visinfo, Heads[0].Context, True);
142   }
143   if (!ctx) {
144      Error(displayName, "Couldn't create GLX context");
145      return NULL;
146   }
147
148   XMapWindow(dpy, win);
149
150   if (!glXMakeCurrent(dpy, win, ctx)) {
151      Error(displayName, "glXMakeCurrent failed");
152      printf("glXMakeCurrent failed in Redraw()\n");
153      return NULL;
154   }
155
156   if (NumHeads == 0) {
157      /* create texture object now */
158      static const GLubyte checker[2][2][4] = {
159         { {255, 255, 255, 255}, {  0,   0,   0, 255} },
160         { {  0,   0,   0,   0}, {255, 255, 255, 255} }
161      };
162      glGenTextures(1, &TexObj);
163      assert(TexObj);
164      glBindTexture(GL_TEXTURE_2D, TexObj);
165      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGB,
166                   GL_UNSIGNED_BYTE, checker);
167      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
168      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
169   }
170   else {
171      /* bind 0th context's texture in this context too */
172      assert(TexObj);
173      glBindTexture(GL_TEXTURE_2D, TexObj);
174   }
175   glEnable(GL_TEXTURE_2D);
176
177   /* save the info for this head */
178   {
179      struct head *h = &Heads[NumHeads];
180      const char * tmp;
181
182      if (strlen(name) + 1 > sizeof(h->DisplayName)) {
183         Error(displayName, "name string overflow");
184         return NULL;
185      }
186      strcpy(h->DisplayName, name);
187
188      h->Dpy = dpy;
189      h->Win = win;
190      h->Context = ctx;
191      h->Angle = 0.0;
192
193      tmp = (char *) glGetString(GL_VERSION);
194      if (strlen(tmp) + 1 > sizeof(h->Version)) {
195         Error(displayName, "GL_VERSION string overflow");
196         return NULL;
197      }
198      strcpy(h->Version, tmp);
199
200      tmp = (char *) glGetString(GL_VENDOR);
201      if (strlen(tmp) + 1 > sizeof(h->Vendor)) {
202         Error(displayName, "GL_VENDOR string overflow");
203         return NULL;
204      }
205      strcpy(h->Vendor, tmp);
206
207      tmp = (char *) glGetString(GL_RENDERER);
208      if (strlen(tmp) + 1 > sizeof(h->Renderer)) {
209         Error(displayName, "GL_RENDERER string overflow");
210         return NULL;
211      }
212      strcpy(h->Renderer, tmp);
213
214      NumHeads++;
215      return &Heads[NumHeads-1];
216   }
217
218}
219
220
221static void
222DestroyHeads(void)
223{
224   int i;
225   for (i = 0; i < NumHeads; i++) {
226      XDestroyWindow(Heads[i].Dpy, Heads[i].Win);
227      glXDestroyContext(Heads[i].Dpy, Heads[i].Context);
228      XCloseDisplay(Heads[i].Dpy);
229   }
230}
231
232
233static void
234Redraw(struct head *h)
235{
236   if (!glXMakeCurrent(h->Dpy, h->Win, h->Context)) {
237      Error(h->DisplayName, "glXMakeCurrent failed");
238      printf("glXMakeCurrent failed in Redraw()\n");
239      return;
240   }
241
242   h->Angle += 1.0;
243
244   glShadeModel(GL_FLAT);
245   glClearColor(0.5, 0.5, 0.5, 1.0);
246   glClear(GL_COLOR_BUFFER_BIT);
247
248   /* draw green triangle */
249   glColor3f(0.0, 1.0, 0.0);
250   glPushMatrix();
251   glRotatef(h->Angle, 0, 0, 1);
252   glBegin(GL_TRIANGLES);
253   glTexCoord2f(0.5, 1.0);   glVertex2f(0, 0.8);
254   glTexCoord2f(0.0, 0.0);   glVertex2f(-0.8, -0.7);
255   glTexCoord2f(1.0, 0.0);   glVertex2f(0.8, -0.7);
256   glEnd();
257   glPopMatrix();
258
259   if (!SwapSeparate)
260      glXSwapBuffers(h->Dpy, h->Win);
261}
262
263
264static void
265Swap(struct head *h)
266{
267   glXSwapBuffers(h->Dpy, h->Win);
268}
269
270
271static void
272Resize(const struct head *h, unsigned int width, unsigned int height)
273{
274   if (!glXMakeCurrent(h->Dpy, h->Win, h->Context)) {
275      Error(h->DisplayName, "glXMakeCurrent failed in Resize()");
276      return;
277   }
278   glFlush();
279   glViewport(0, 0, width, height);
280   glMatrixMode(GL_PROJECTION);
281   glLoadIdentity();
282   glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
283}
284
285
286
287static void
288EventLoop(void)
289{
290   while (1) {
291      int i;
292      for (i = 0; i < NumHeads; i++) {
293         struct head *h = &Heads[i];
294         while (XPending(h->Dpy) > 0) {
295            XEvent event;
296            XNextEvent(h->Dpy, &event);
297            if (event.xany.window == h->Win) {
298               switch (event.type) {
299                  case Expose:
300                     Redraw(h);
301                     if (SwapSeparate)
302                        Swap(h);
303                     break;
304                  case ConfigureNotify:
305                     Resize(h, event.xconfigure.width, event.xconfigure.height);
306                     break;
307                  case KeyPress:
308                     {
309                        char buf[100];
310                        KeySym keySym;
311                        XComposeStatus stat;
312                        XLookupString(&event.xkey, buf, sizeof(buf), &keySym, &stat);
313                        switch (keySym) {
314                           case XK_Escape:
315                              return;
316                              break;
317                           case XK_d:
318                           case XK_D:
319                              printf("Delete Texture in window %d\n", i);
320                              glXMakeCurrent(h->Dpy, h->Win, h->Context);
321                              glDeleteTextures(1, &TexObj);
322                              break;
323                           case XK_u:
324                           case XK_U:
325                              printf("Unbind Texture in window %d\n", i);
326                              glXMakeCurrent(h->Dpy, h->Win, h->Context);
327                              glBindTexture(GL_TEXTURE_2D, 0);
328                              break;
329                        }
330                     }
331                     break;
332                  default:
333                     /*no-op*/ ;
334               }
335            }
336            else {
337               printf("window mismatch\n");
338            }
339         }
340      }
341
342      /* redraw all windows */
343      for (i = 0; i < NumHeads; i++) {
344         Redraw(&Heads[i]);
345      }
346      /* swapbuffers on all windows, if not already done */
347      if (SwapSeparate) {
348         for (i = 0; i < NumHeads; i++) {
349            Swap(&Heads[i]);
350         }
351      }
352      usleep(1);
353   }
354}
355
356
357
358static void
359PrintInfo(const struct head *h)
360{
361   printf("Name: %s\n", h->DisplayName);
362   printf("  Display:     %p\n", (void *) h->Dpy);
363   printf("  Window:      0x%x\n", (int) h->Win);
364   printf("  Context:     0x%lx\n", (long) h->Context);
365   printf("  GL_VERSION:  %s\n", h->Version);
366   printf("  GL_VENDOR:   %s\n", h->Vendor);
367   printf("  GL_RENDERER: %s\n", h->Renderer);
368}
369
370
371int
372main(int argc, char *argv[])
373{
374   char *dpyName = NULL;
375   int i;
376
377   if (argc == 1) {
378      printf("manywin: open N simultaneous glx windows\n");
379      printf("Usage:\n");
380      printf("  manywin [-s] numWindows\n");
381      printf("Options:\n");
382      printf("  -s = swap immediately after drawing (see src code)\n");
383      printf("Example:\n");
384      printf("  manywin 10\n");
385      return 0;
386   }
387   else {
388      int n = 3;
389      for (i = 1; i < argc; i++) {
390         if (strcmp(argv[i], "-s") == 0) {
391            SwapSeparate = GL_FALSE;
392         }
393         else if (strcmp(argv[i], "-display") == 0 && i < argc) {
394            dpyName = argv[i+1];
395            i++;
396         }
397         else {
398            n = atoi(argv[i]);
399         }
400      }
401      if (n < 1)
402         n = 1;
403      if (n > MAX_HEADS)
404         n = MAX_HEADS;
405
406      printf("%d windows\n", n);
407      for (i = 0; i < n; i++) {
408         char name[100];
409         struct head *h;
410         sprintf(name, "%d", i);
411         h = AddHead(dpyName, name);
412         if (h) {
413            PrintInfo(h);
414         }
415      }
416   }
417
418   EventLoop();
419   DestroyHeads();
420   return 0;
421}
422