132001f49Smrg/*
232001f49Smrg * Create N GLX windows/contexts and render to them in round-robin order.
332001f49Smrg * Also, have the contexts share all texture objects.
432001f49Smrg * Press 'd' to delete a texture, 'u' to unbind it.
532001f49Smrg *
632001f49Smrg * Copyright (C) 2000  Brian Paul   All Rights Reserved.
732001f49Smrg *
832001f49Smrg * Permission is hereby granted, free of charge, to any person obtaining a
932001f49Smrg * copy of this software and associated documentation files (the "Software"),
1032001f49Smrg * to deal in the Software without restriction, including without limitation
1132001f49Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1232001f49Smrg * and/or sell copies of the Software, and to permit persons to whom the
1332001f49Smrg * Software is furnished to do so, subject to the following conditions:
1432001f49Smrg *
1532001f49Smrg * The above copyright notice and this permission notice shall be included
1632001f49Smrg * in all copies or substantial portions of the Software.
1732001f49Smrg *
1832001f49Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1932001f49Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2032001f49Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
2132001f49Smrg * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
2232001f49Smrg * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
2332001f49Smrg * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2432001f49Smrg */
2532001f49Smrg
2632001f49Smrg
2732001f49Smrg#include <GL/gl.h>
2832001f49Smrg#include <GL/glx.h>
2932001f49Smrg#include <assert.h>
3032001f49Smrg#include <stdio.h>
3132001f49Smrg#include <stdlib.h>
3232001f49Smrg#include <string.h>
3332001f49Smrg#include <unistd.h>
3432001f49Smrg#include <X11/keysym.h>
3532001f49Smrg
3632001f49Smrg
3732001f49Smrg/*
3832001f49Smrg * Each display/window/context:
3932001f49Smrg */
4032001f49Smrgstruct head {
4132001f49Smrg   char DisplayName[1000];
4232001f49Smrg   Display *Dpy;
4332001f49Smrg   Window Win;
4432001f49Smrg   GLXContext Context;
4532001f49Smrg   float Angle;
4632001f49Smrg   char Renderer[1000];
4732001f49Smrg   char Vendor[1000];
4832001f49Smrg   char Version[1000];
4932001f49Smrg};
5032001f49Smrg
5132001f49Smrg
5232001f49Smrg#define MAX_HEADS 200
5332001f49Smrgstatic struct head Heads[MAX_HEADS];
5432001f49Smrgstatic int NumHeads = 0;
5532001f49Smrgstatic GLboolean SwapSeparate = GL_TRUE;
5632001f49Smrgstatic GLuint TexObj = 0;
5732001f49Smrg
5832001f49Smrg
5932001f49Smrgstatic void
6032001f49SmrgError(const char *display, const char *msg)
6132001f49Smrg{
6232001f49Smrg   fprintf(stderr, "Error on display %s - %s\n", XDisplayName(display), msg);
6332001f49Smrg   exit(1);
6432001f49Smrg}
6532001f49Smrg
6632001f49Smrg
6732001f49Smrgstatic struct head *
6832001f49SmrgAddHead(const char *displayName, const char *name)
6932001f49Smrg{
7032001f49Smrg   Display *dpy;
7132001f49Smrg   Window win;
7232001f49Smrg   GLXContext ctx;
7332001f49Smrg   int attrib[] = { GLX_RGBA,
7432001f49Smrg		    GLX_RED_SIZE, 1,
7532001f49Smrg		    GLX_GREEN_SIZE, 1,
7632001f49Smrg		    GLX_BLUE_SIZE, 1,
7732001f49Smrg		    GLX_DOUBLEBUFFER,
7832001f49Smrg		    None };
7932001f49Smrg   int scrnum;
8032001f49Smrg   XSetWindowAttributes attr;
8132001f49Smrg   unsigned long mask;
8232001f49Smrg   Window root;
8332001f49Smrg   XVisualInfo *visinfo;
8432001f49Smrg   int width = 90, height = 90;
8532001f49Smrg   int xpos = 0, ypos = 0;
8632001f49Smrg
8732001f49Smrg   if (NumHeads >= MAX_HEADS)
8832001f49Smrg      return NULL;
8932001f49Smrg
9032001f49Smrg   dpy = XOpenDisplay(displayName);
9132001f49Smrg   if (!dpy) {
9232001f49Smrg      Error(displayName, "Unable to open display");
9332001f49Smrg      return NULL;
9432001f49Smrg   }
9532001f49Smrg
9632001f49Smrg   scrnum = DefaultScreen(dpy);
9732001f49Smrg   root = RootWindow(dpy, scrnum);
9832001f49Smrg
9932001f49Smrg   visinfo = glXChooseVisual(dpy, scrnum, attrib);
10032001f49Smrg   if (!visinfo) {
10132001f49Smrg      Error(displayName, "Unable to find RGB, double-buffered visual");
10232001f49Smrg      return NULL;
10332001f49Smrg   }
10432001f49Smrg
10532001f49Smrg   /* window attributes */
10632001f49Smrg   xpos = (NumHeads % 10) * 100;
10732001f49Smrg   ypos = (NumHeads / 10) * 100;
10832001f49Smrg   printf("%d, %d\n", xpos, ypos);
10932001f49Smrg   attr.background_pixel = 0;
11032001f49Smrg   attr.border_pixel = 0;
11132001f49Smrg   attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone);
11232001f49Smrg   attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
11332001f49Smrg   mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
11432001f49Smrg
11532001f49Smrg   win = XCreateWindow(dpy, root, xpos, ypos, width, height,
11632001f49Smrg		        0, visinfo->depth, InputOutput,
11732001f49Smrg		        visinfo->visual, mask, &attr);
11832001f49Smrg   if (!win) {
11932001f49Smrg      Error(displayName, "Couldn't create window");
12032001f49Smrg      return NULL;
12132001f49Smrg   }
12232001f49Smrg
12332001f49Smrg   {
12432001f49Smrg      XSizeHints sizehints;
12532001f49Smrg      sizehints.x = xpos;
12632001f49Smrg      sizehints.y = ypos;
12732001f49Smrg      sizehints.width  = width;
12832001f49Smrg      sizehints.height = height;
12932001f49Smrg      sizehints.flags = USSize | USPosition;
13032001f49Smrg      XSetNormalHints(dpy, win, &sizehints);
13132001f49Smrg      XSetStandardProperties(dpy, win, name, name,
13232001f49Smrg                              None, (char **)NULL, 0, &sizehints);
13332001f49Smrg   }
13432001f49Smrg
13532001f49Smrg   if (NumHeads == 0) {
13632001f49Smrg      ctx = glXCreateContext(dpy, visinfo, NULL, True);
13732001f49Smrg   }
13832001f49Smrg   else {
13932001f49Smrg      /* share textures & dlists with 0th context */
14032001f49Smrg      printf("sharing\n");
14132001f49Smrg      ctx = glXCreateContext(dpy, visinfo, Heads[0].Context, True);
14232001f49Smrg   }
14332001f49Smrg   if (!ctx) {
14432001f49Smrg      Error(displayName, "Couldn't create GLX context");
14532001f49Smrg      return NULL;
14632001f49Smrg   }
14732001f49Smrg
14832001f49Smrg   XMapWindow(dpy, win);
14932001f49Smrg
15032001f49Smrg   if (!glXMakeCurrent(dpy, win, ctx)) {
15132001f49Smrg      Error(displayName, "glXMakeCurrent failed");
15232001f49Smrg      printf("glXMakeCurrent failed in Redraw()\n");
15332001f49Smrg      return NULL;
15432001f49Smrg   }
15532001f49Smrg
15632001f49Smrg   if (NumHeads == 0) {
15732001f49Smrg      /* create texture object now */
15832001f49Smrg      static const GLubyte checker[2][2][4] = {
15932001f49Smrg         { {255, 255, 255, 255}, {  0,   0,   0, 255} },
16032001f49Smrg         { {  0,   0,   0,   0}, {255, 255, 255, 255} }
16132001f49Smrg      };
16232001f49Smrg      glGenTextures(1, &TexObj);
16332001f49Smrg      assert(TexObj);
16432001f49Smrg      glBindTexture(GL_TEXTURE_2D, TexObj);
16532001f49Smrg      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGB,
16632001f49Smrg                   GL_UNSIGNED_BYTE, checker);
16732001f49Smrg      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
16832001f49Smrg      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
16932001f49Smrg   }
17032001f49Smrg   else {
17132001f49Smrg      /* bind 0th context's texture in this context too */
17232001f49Smrg      assert(TexObj);
17332001f49Smrg      glBindTexture(GL_TEXTURE_2D, TexObj);
17432001f49Smrg   }
17532001f49Smrg   glEnable(GL_TEXTURE_2D);
17632001f49Smrg
17732001f49Smrg   /* save the info for this head */
17832001f49Smrg   {
17932001f49Smrg      struct head *h = &Heads[NumHeads];
18032001f49Smrg      const char * tmp;
18132001f49Smrg
18232001f49Smrg      if (strlen(name) + 1 > sizeof(h->DisplayName)) {
18332001f49Smrg         Error(displayName, "name string overflow");
18432001f49Smrg         return NULL;
18532001f49Smrg      }
18632001f49Smrg      strcpy(h->DisplayName, name);
18732001f49Smrg
18832001f49Smrg      h->Dpy = dpy;
18932001f49Smrg      h->Win = win;
19032001f49Smrg      h->Context = ctx;
19132001f49Smrg      h->Angle = 0.0;
19232001f49Smrg
19332001f49Smrg      tmp = (char *) glGetString(GL_VERSION);
19432001f49Smrg      if (strlen(tmp) + 1 > sizeof(h->Version)) {
19532001f49Smrg         Error(displayName, "GL_VERSION string overflow");
19632001f49Smrg         return NULL;
19732001f49Smrg      }
19832001f49Smrg      strcpy(h->Version, tmp);
19932001f49Smrg
20032001f49Smrg      tmp = (char *) glGetString(GL_VENDOR);
20132001f49Smrg      if (strlen(tmp) + 1 > sizeof(h->Vendor)) {
20232001f49Smrg         Error(displayName, "GL_VENDOR string overflow");
20332001f49Smrg         return NULL;
20432001f49Smrg      }
20532001f49Smrg      strcpy(h->Vendor, tmp);
20632001f49Smrg
20732001f49Smrg      tmp = (char *) glGetString(GL_RENDERER);
20832001f49Smrg      if (strlen(tmp) + 1 > sizeof(h->Renderer)) {
20932001f49Smrg         Error(displayName, "GL_RENDERER string overflow");
21032001f49Smrg         return NULL;
21132001f49Smrg      }
21232001f49Smrg      strcpy(h->Renderer, tmp);
21332001f49Smrg
21432001f49Smrg      NumHeads++;
21532001f49Smrg      return &Heads[NumHeads-1];
21632001f49Smrg   }
21732001f49Smrg
21832001f49Smrg}
21932001f49Smrg
22032001f49Smrg
22132001f49Smrgstatic void
22232001f49SmrgDestroyHeads(void)
22332001f49Smrg{
22432001f49Smrg   int i;
22532001f49Smrg   for (i = 0; i < NumHeads; i++) {
22632001f49Smrg      XDestroyWindow(Heads[i].Dpy, Heads[i].Win);
22732001f49Smrg      glXDestroyContext(Heads[i].Dpy, Heads[i].Context);
22832001f49Smrg      XCloseDisplay(Heads[i].Dpy);
22932001f49Smrg   }
23032001f49Smrg}
23132001f49Smrg
23232001f49Smrg
23332001f49Smrgstatic void
23432001f49SmrgRedraw(struct head *h)
23532001f49Smrg{
23632001f49Smrg   if (!glXMakeCurrent(h->Dpy, h->Win, h->Context)) {
23732001f49Smrg      Error(h->DisplayName, "glXMakeCurrent failed");
23832001f49Smrg      printf("glXMakeCurrent failed in Redraw()\n");
23932001f49Smrg      return;
24032001f49Smrg   }
24132001f49Smrg
24232001f49Smrg   h->Angle += 1.0;
24332001f49Smrg
24432001f49Smrg   glShadeModel(GL_FLAT);
24532001f49Smrg   glClearColor(0.5, 0.5, 0.5, 1.0);
24632001f49Smrg   glClear(GL_COLOR_BUFFER_BIT);
24732001f49Smrg
24832001f49Smrg   /* draw green triangle */
24932001f49Smrg   glColor3f(0.0, 1.0, 0.0);
25032001f49Smrg   glPushMatrix();
25132001f49Smrg   glRotatef(h->Angle, 0, 0, 1);
25232001f49Smrg   glBegin(GL_TRIANGLES);
25332001f49Smrg   glTexCoord2f(0.5, 1.0);   glVertex2f(0, 0.8);
25432001f49Smrg   glTexCoord2f(0.0, 0.0);   glVertex2f(-0.8, -0.7);
25532001f49Smrg   glTexCoord2f(1.0, 0.0);   glVertex2f(0.8, -0.7);
25632001f49Smrg   glEnd();
25732001f49Smrg   glPopMatrix();
25832001f49Smrg
25932001f49Smrg   if (!SwapSeparate)
26032001f49Smrg      glXSwapBuffers(h->Dpy, h->Win);
26132001f49Smrg}
26232001f49Smrg
26332001f49Smrg
26432001f49Smrgstatic void
26532001f49SmrgSwap(struct head *h)
26632001f49Smrg{
26732001f49Smrg   glXSwapBuffers(h->Dpy, h->Win);
26832001f49Smrg}
26932001f49Smrg
27032001f49Smrg
27132001f49Smrgstatic void
27232001f49SmrgResize(const struct head *h, unsigned int width, unsigned int height)
27332001f49Smrg{
27432001f49Smrg   if (!glXMakeCurrent(h->Dpy, h->Win, h->Context)) {
27532001f49Smrg      Error(h->DisplayName, "glXMakeCurrent failed in Resize()");
27632001f49Smrg      return;
27732001f49Smrg   }
27832001f49Smrg   glFlush();
27932001f49Smrg   glViewport(0, 0, width, height);
28032001f49Smrg   glMatrixMode(GL_PROJECTION);
28132001f49Smrg   glLoadIdentity();
28232001f49Smrg   glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
28332001f49Smrg}
28432001f49Smrg
28532001f49Smrg
28632001f49Smrg
28732001f49Smrgstatic void
28832001f49SmrgEventLoop(void)
28932001f49Smrg{
29032001f49Smrg   while (1) {
29132001f49Smrg      int i;
29232001f49Smrg      for (i = 0; i < NumHeads; i++) {
29332001f49Smrg         struct head *h = &Heads[i];
29432001f49Smrg         while (XPending(h->Dpy) > 0) {
29532001f49Smrg            XEvent event;
29632001f49Smrg            XNextEvent(h->Dpy, &event);
29732001f49Smrg            if (event.xany.window == h->Win) {
29832001f49Smrg               switch (event.type) {
29932001f49Smrg                  case Expose:
30032001f49Smrg                     Redraw(h);
30132001f49Smrg                     if (SwapSeparate)
30232001f49Smrg                        Swap(h);
30332001f49Smrg                     break;
30432001f49Smrg                  case ConfigureNotify:
30532001f49Smrg                     Resize(h, event.xconfigure.width, event.xconfigure.height);
30632001f49Smrg                     break;
30732001f49Smrg                  case KeyPress:
30832001f49Smrg                     {
30932001f49Smrg                        char buf[100];
31032001f49Smrg                        KeySym keySym;
31132001f49Smrg                        XComposeStatus stat;
31232001f49Smrg                        XLookupString(&event.xkey, buf, sizeof(buf), &keySym, &stat);
31332001f49Smrg                        switch (keySym) {
31432001f49Smrg                           case XK_Escape:
31532001f49Smrg                              return;
31632001f49Smrg                              break;
31732001f49Smrg                           case XK_d:
31832001f49Smrg                           case XK_D:
31932001f49Smrg                              printf("Delete Texture in window %d\n", i);
32032001f49Smrg                              glXMakeCurrent(h->Dpy, h->Win, h->Context);
32132001f49Smrg                              glDeleteTextures(1, &TexObj);
32232001f49Smrg                              break;
32332001f49Smrg                           case XK_u:
32432001f49Smrg                           case XK_U:
32532001f49Smrg                              printf("Unbind Texture in window %d\n", i);
32632001f49Smrg                              glXMakeCurrent(h->Dpy, h->Win, h->Context);
32732001f49Smrg                              glBindTexture(GL_TEXTURE_2D, 0);
32832001f49Smrg                              break;
32932001f49Smrg                        }
33032001f49Smrg                     }
33132001f49Smrg                     break;
33232001f49Smrg                  default:
33332001f49Smrg                     /*no-op*/ ;
33432001f49Smrg               }
33532001f49Smrg            }
33632001f49Smrg            else {
33732001f49Smrg               printf("window mismatch\n");
33832001f49Smrg            }
33932001f49Smrg         }
34032001f49Smrg      }
34132001f49Smrg
34232001f49Smrg      /* redraw all windows */
34332001f49Smrg      for (i = 0; i < NumHeads; i++) {
34432001f49Smrg         Redraw(&Heads[i]);
34532001f49Smrg      }
34632001f49Smrg      /* swapbuffers on all windows, if not already done */
34732001f49Smrg      if (SwapSeparate) {
34832001f49Smrg         for (i = 0; i < NumHeads; i++) {
34932001f49Smrg            Swap(&Heads[i]);
35032001f49Smrg         }
35132001f49Smrg      }
35232001f49Smrg      usleep(1);
35332001f49Smrg   }
35432001f49Smrg}
35532001f49Smrg
35632001f49Smrg
35732001f49Smrg
35832001f49Smrgstatic void
35932001f49SmrgPrintInfo(const struct head *h)
36032001f49Smrg{
36132001f49Smrg   printf("Name: %s\n", h->DisplayName);
36232001f49Smrg   printf("  Display:     %p\n", (void *) h->Dpy);
36332001f49Smrg   printf("  Window:      0x%x\n", (int) h->Win);
36432001f49Smrg   printf("  Context:     0x%lx\n", (long) h->Context);
36532001f49Smrg   printf("  GL_VERSION:  %s\n", h->Version);
36632001f49Smrg   printf("  GL_VENDOR:   %s\n", h->Vendor);
36732001f49Smrg   printf("  GL_RENDERER: %s\n", h->Renderer);
36832001f49Smrg}
36932001f49Smrg
37032001f49Smrg
37132001f49Smrgint
37232001f49Smrgmain(int argc, char *argv[])
37332001f49Smrg{
37432001f49Smrg   char *dpyName = NULL;
37532001f49Smrg   int i;
37632001f49Smrg
37732001f49Smrg   if (argc == 1) {
37832001f49Smrg      printf("manywin: open N simultaneous glx windows\n");
37932001f49Smrg      printf("Usage:\n");
38032001f49Smrg      printf("  manywin [-s] numWindows\n");
38132001f49Smrg      printf("Options:\n");
38232001f49Smrg      printf("  -s = swap immediately after drawing (see src code)\n");
38332001f49Smrg      printf("Example:\n");
38432001f49Smrg      printf("  manywin 10\n");
38532001f49Smrg      return 0;
38632001f49Smrg   }
38732001f49Smrg   else {
38832001f49Smrg      int n = 3;
38932001f49Smrg      for (i = 1; i < argc; i++) {
39032001f49Smrg         if (strcmp(argv[i], "-s") == 0) {
39132001f49Smrg            SwapSeparate = GL_FALSE;
39232001f49Smrg         }
39332001f49Smrg         else if (strcmp(argv[i], "-display") == 0 && i < argc) {
39432001f49Smrg            dpyName = argv[i+1];
39532001f49Smrg            i++;
39632001f49Smrg         }
39732001f49Smrg         else {
39832001f49Smrg            n = atoi(argv[i]);
39932001f49Smrg         }
40032001f49Smrg      }
40132001f49Smrg      if (n < 1)
40232001f49Smrg         n = 1;
40332001f49Smrg      if (n > MAX_HEADS)
40432001f49Smrg         n = MAX_HEADS;
40532001f49Smrg
40632001f49Smrg      printf("%d windows\n", n);
40732001f49Smrg      for (i = 0; i < n; i++) {
40832001f49Smrg         char name[100];
40932001f49Smrg         struct head *h;
41032001f49Smrg         sprintf(name, "%d", i);
41132001f49Smrg         h = AddHead(dpyName, name);
41232001f49Smrg         if (h) {
41332001f49Smrg            PrintInfo(h);
41432001f49Smrg         }
41532001f49Smrg      }
41632001f49Smrg   }
41732001f49Smrg
41832001f49Smrg   EventLoop();
41932001f49Smrg   DestroyHeads();
42032001f49Smrg   return 0;
42132001f49Smrg}
422