132001f49Smrg/*
232001f49Smrg * Copyright (C) 2000  Brian Paul   All Rights Reserved.
332001f49Smrg *
432001f49Smrg * Permission is hereby granted, free of charge, to any person obtaining a
532001f49Smrg * copy of this software and associated documentation files (the "Software"),
632001f49Smrg * to deal in the Software without restriction, including without limitation
732001f49Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
832001f49Smrg * and/or sell copies of the Software, and to permit persons to whom the
932001f49Smrg * Software is furnished to do so, subject to the following conditions:
1032001f49Smrg *
1132001f49Smrg * The above copyright notice and this permission notice shall be included
1232001f49Smrg * in all copies or substantial portions of the Software.
1332001f49Smrg *
1432001f49Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1532001f49Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1632001f49Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1732001f49Smrg * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
1832001f49Smrg * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
1932001f49Smrg * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2032001f49Smrg */
2132001f49Smrg
2232001f49Smrg
2332001f49Smrg/*
2432001f49Smrg * This program tests GLX thread safety.
2532001f49Smrg * Command line options:
2632001f49Smrg *  -p                       Open a display connection for each thread
2732001f49Smrg *  -l                       Enable application-side locking
2832001f49Smrg *  -n <num threads>         Number of threads to create (default is 2)
2932001f49Smrg *  -display <display name>  Specify X display (default is $DISPLAY)
3032001f49Smrg *  -t                       Use texture mapping
3132001f49Smrg *
3232001f49Smrg * Brian Paul  20 July 2000
3332001f49Smrg */
3432001f49Smrg
3532001f49Smrg
3632001f49Smrg/*
3732001f49Smrg * Notes:
3832001f49Smrg * - Each thread gets its own GLX context.
3932001f49Smrg *
4032001f49Smrg * - The GLX contexts share texture objects.
4132001f49Smrg *
4232001f49Smrg * - When 't' is pressed to update the texture image, the window/thread which
4332001f49Smrg *   has input focus is signalled to change the texture.  The other threads
4432001f49Smrg *   should see the updated texture the next time they call glBindTexture.
4532001f49Smrg */
4632001f49Smrg
4732001f49Smrg
4832001f49Smrg#include <assert.h>
4932001f49Smrg#include <GL/gl.h>
5032001f49Smrg#include <GL/glx.h>
5132001f49Smrg#include <math.h>
5232001f49Smrg#include <stdio.h>
5332001f49Smrg#include <stdlib.h>
5432001f49Smrg#include <string.h>
5532001f49Smrg#include <unistd.h>
5632001f49Smrg#include <pthread.h>
5732001f49Smrg
5832001f49Smrg
5932001f49Smrg/*
6032001f49Smrg * Each window/thread/context:
6132001f49Smrg */
6232001f49Smrgstruct winthread {
6332001f49Smrg   Display *Dpy;
6432001f49Smrg   int Index;
6532001f49Smrg   pthread_t Thread;
6632001f49Smrg   Window Win;
6732001f49Smrg   GLXContext Context;
6832001f49Smrg   float Angle;
6932001f49Smrg   int WinWidth, WinHeight;
7032001f49Smrg   GLboolean NewSize;
7132001f49Smrg   GLboolean Initialized;
7232001f49Smrg   GLboolean MakeNewTexture;
7332001f49Smrg};
7432001f49Smrg
7532001f49Smrg
7632001f49Smrg#define MAX_WINTHREADS 100
7732001f49Smrgstatic struct winthread WinThreads[MAX_WINTHREADS];
7832001f49Smrgstatic int NumWinThreads = 0;
7932001f49Smrgstatic volatile GLboolean ExitFlag = GL_FALSE;
8032001f49Smrg
8132001f49Smrgstatic GLboolean MultiDisplays = 0;
8232001f49Smrgstatic GLboolean Locking = 0;
8332001f49Smrgstatic GLboolean Texture = GL_FALSE;
8432001f49Smrgstatic GLuint TexObj = 12;
8532001f49Smrgstatic GLboolean Animate = GL_TRUE;
8632001f49Smrg
8732001f49Smrgstatic pthread_mutex_t Mutex;
8832001f49Smrgstatic pthread_cond_t CondVar;
8932001f49Smrgstatic pthread_mutex_t CondMutex;
9032001f49Smrg
9132001f49Smrg
9232001f49Smrgstatic void
9332001f49SmrgError(const char *msg)
9432001f49Smrg{
9532001f49Smrg   fprintf(stderr, "Error: %s\n", msg);
9632001f49Smrg   exit(1);
9732001f49Smrg}
9832001f49Smrg
9932001f49Smrg
10032001f49Smrgstatic void
10132001f49Smrgsignal_redraw(void)
10232001f49Smrg{
10332001f49Smrg   pthread_mutex_lock(&CondMutex);
10432001f49Smrg   pthread_cond_broadcast(&CondVar);
10532001f49Smrg   pthread_mutex_unlock(&CondMutex);
10632001f49Smrg}
10732001f49Smrg
10832001f49Smrg
10932001f49Smrgstatic void
11032001f49SmrgMakeNewTexture(struct winthread *wt)
11132001f49Smrg{
11232001f49Smrg#define TEX_SIZE 128
11332001f49Smrg   static float step = 0.0;
11432001f49Smrg   GLfloat image[TEX_SIZE][TEX_SIZE][4];
11532001f49Smrg   GLint width;
11632001f49Smrg   int i, j;
11732001f49Smrg
11832001f49Smrg   for (j = 0; j < TEX_SIZE; j++) {
11932001f49Smrg      for (i = 0; i < TEX_SIZE; i++) {
12032001f49Smrg         float dt = 5.0 * (j - 0.5 * TEX_SIZE) / TEX_SIZE;
12132001f49Smrg         float ds = 5.0 * (i - 0.5 * TEX_SIZE) / TEX_SIZE;
12232001f49Smrg         float r = dt * dt + ds * ds + step;
12332001f49Smrg         image[j][i][0] =
12432001f49Smrg         image[j][i][1] =
12532001f49Smrg         image[j][i][2] = 0.75 + 0.25 * cos(r);
12632001f49Smrg         image[j][i][3] = 1.0;
12732001f49Smrg      }
12832001f49Smrg   }
12932001f49Smrg
13032001f49Smrg   step += 0.5;
13132001f49Smrg
13232001f49Smrg   glBindTexture(GL_TEXTURE_2D, TexObj);
13332001f49Smrg
13432001f49Smrg   glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
13532001f49Smrg   if (width) {
13632001f49Smrg      assert(width == TEX_SIZE);
13732001f49Smrg      /* sub-tex replace */
13832001f49Smrg      glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, TEX_SIZE, TEX_SIZE,
13932001f49Smrg                   GL_RGBA, GL_FLOAT, image);
14032001f49Smrg   }
14132001f49Smrg   else {
14232001f49Smrg      /* create new */
14332001f49Smrg      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
14432001f49Smrg      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
14532001f49Smrg
14632001f49Smrg      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEX_SIZE, TEX_SIZE, 0,
14732001f49Smrg                   GL_RGBA, GL_FLOAT, image);
14832001f49Smrg   }
14932001f49Smrg}
15032001f49Smrg
15132001f49Smrg
15232001f49Smrg
15332001f49Smrg/* draw a colored cube */
15432001f49Smrgstatic void
15532001f49Smrgdraw_object(void)
15632001f49Smrg{
15732001f49Smrg   glPushMatrix();
15832001f49Smrg   glScalef(0.75, 0.75, 0.75);
15932001f49Smrg
16032001f49Smrg   glColor3f(1, 0, 0);
16132001f49Smrg
16232001f49Smrg   if (Texture) {
16332001f49Smrg      glBindTexture(GL_TEXTURE_2D, TexObj);
16432001f49Smrg      glEnable(GL_TEXTURE_2D);
16532001f49Smrg   }
16632001f49Smrg   else {
16732001f49Smrg      glDisable(GL_TEXTURE_2D);
16832001f49Smrg   }
16932001f49Smrg
17032001f49Smrg   glBegin(GL_QUADS);
17132001f49Smrg
17232001f49Smrg   /* -X */
17332001f49Smrg   glColor3f(0, 1, 1);
17432001f49Smrg   glTexCoord2f(0, 0);  glVertex3f(-1, -1, -1);
17532001f49Smrg   glTexCoord2f(1, 0);  glVertex3f(-1,  1, -1);
17632001f49Smrg   glTexCoord2f(1, 1);  glVertex3f(-1,  1,  1);
17732001f49Smrg   glTexCoord2f(0, 1);  glVertex3f(-1, -1,  1);
17832001f49Smrg
17932001f49Smrg   /* +X */
18032001f49Smrg   glColor3f(1, 0, 0);
18132001f49Smrg   glTexCoord2f(0, 0);  glVertex3f(1, -1, -1);
18232001f49Smrg   glTexCoord2f(1, 0);  glVertex3f(1,  1, -1);
18332001f49Smrg   glTexCoord2f(1, 1);  glVertex3f(1,  1,  1);
18432001f49Smrg   glTexCoord2f(0, 1);  glVertex3f(1, -1,  1);
18532001f49Smrg
18632001f49Smrg   /* -Y */
18732001f49Smrg   glColor3f(1, 0, 1);
18832001f49Smrg   glTexCoord2f(0, 0);  glVertex3f(-1, -1, -1);
18932001f49Smrg   glTexCoord2f(1, 0);  glVertex3f( 1, -1, -1);
19032001f49Smrg   glTexCoord2f(1, 1);  glVertex3f( 1, -1,  1);
19132001f49Smrg   glTexCoord2f(0, 1);  glVertex3f(-1, -1,  1);
19232001f49Smrg
19332001f49Smrg   /* +Y */
19432001f49Smrg   glColor3f(0, 1, 0);
19532001f49Smrg   glTexCoord2f(0, 0);  glVertex3f(-1, 1, -1);
19632001f49Smrg   glTexCoord2f(1, 0);  glVertex3f( 1, 1, -1);
19732001f49Smrg   glTexCoord2f(1, 1);  glVertex3f( 1, 1,  1);
19832001f49Smrg   glTexCoord2f(0, 1);  glVertex3f(-1, 1,  1);
19932001f49Smrg
20032001f49Smrg   /* -Z */
20132001f49Smrg   glColor3f(1, 1, 0);
20232001f49Smrg   glTexCoord2f(0, 0);  glVertex3f(-1, -1, -1);
20332001f49Smrg   glTexCoord2f(1, 0);  glVertex3f( 1, -1, -1);
20432001f49Smrg   glTexCoord2f(1, 1);  glVertex3f( 1,  1, -1);
20532001f49Smrg   glTexCoord2f(0, 1);  glVertex3f(-1,  1, -1);
20632001f49Smrg
20732001f49Smrg   /* +Y */
20832001f49Smrg   glColor3f(0, 0, 1);
20932001f49Smrg   glTexCoord2f(0, 0);  glVertex3f(-1, -1, 1);
21032001f49Smrg   glTexCoord2f(1, 0);  glVertex3f( 1, -1, 1);
21132001f49Smrg   glTexCoord2f(1, 1);  glVertex3f( 1,  1, 1);
21232001f49Smrg   glTexCoord2f(0, 1);  glVertex3f(-1,  1, 1);
21332001f49Smrg
21432001f49Smrg   glEnd();
21532001f49Smrg
21632001f49Smrg   glPopMatrix();
21732001f49Smrg}
21832001f49Smrg
21932001f49Smrg
22032001f49Smrg/* signal resize of given window */
22132001f49Smrgstatic void
22232001f49Smrgresize(struct winthread *wt, int w, int h)
22332001f49Smrg{
22432001f49Smrg   wt->NewSize = GL_TRUE;
22532001f49Smrg   wt->WinWidth = w;
22632001f49Smrg   wt->WinHeight = h;
22732001f49Smrg   if (!Animate)
22832001f49Smrg      signal_redraw();
22932001f49Smrg}
23032001f49Smrg
23132001f49Smrg
23232001f49Smrg/*
23332001f49Smrg * We have an instance of this for each thread.
23432001f49Smrg */
23532001f49Smrgstatic void
23632001f49Smrgdraw_loop(struct winthread *wt)
23732001f49Smrg{
23832001f49Smrg   while (!ExitFlag) {
23932001f49Smrg
24032001f49Smrg      if (Locking)
24132001f49Smrg         pthread_mutex_lock(&Mutex);
24232001f49Smrg
24332001f49Smrg      glXMakeCurrent(wt->Dpy, wt->Win, wt->Context);
24432001f49Smrg      if (!wt->Initialized) {
24532001f49Smrg         printf("glthreads: %d: GL_RENDERER = %s\n", wt->Index,
24632001f49Smrg                (char *) glGetString(GL_RENDERER));
24732001f49Smrg         if (Texture /*&& wt->Index == 0*/) {
24832001f49Smrg            MakeNewTexture(wt);
24932001f49Smrg         }
25032001f49Smrg         wt->Initialized = GL_TRUE;
25132001f49Smrg      }
25232001f49Smrg
25332001f49Smrg      if (Locking)
25432001f49Smrg         pthread_mutex_unlock(&Mutex);
25532001f49Smrg
25632001f49Smrg      glEnable(GL_DEPTH_TEST);
25732001f49Smrg
25832001f49Smrg      if (wt->NewSize) {
25932001f49Smrg         GLfloat w = (float) wt->WinWidth / (float) wt->WinHeight;
26032001f49Smrg         glViewport(0, 0, wt->WinWidth, wt->WinHeight);
26132001f49Smrg         glMatrixMode(GL_PROJECTION);
26232001f49Smrg         glLoadIdentity();
26332001f49Smrg         glFrustum(-w, w, -1.0, 1.0, 1.5, 10);
26432001f49Smrg         glMatrixMode(GL_MODELVIEW);
26532001f49Smrg         glLoadIdentity();
26632001f49Smrg         glTranslatef(0, 0, -2.5);
26732001f49Smrg         wt->NewSize = GL_FALSE;
26832001f49Smrg      }
26932001f49Smrg
27032001f49Smrg      if (wt->MakeNewTexture) {
27132001f49Smrg         MakeNewTexture(wt);
27232001f49Smrg         wt->MakeNewTexture = GL_FALSE;
27332001f49Smrg      }
27432001f49Smrg
27532001f49Smrg      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
27632001f49Smrg
27732001f49Smrg      glPushMatrix();
27832001f49Smrg         glRotatef(wt->Angle, 0, 1, 0);
27932001f49Smrg         glRotatef(wt->Angle, 1, 0, 0);
28032001f49Smrg         glScalef(0.7, 0.7, 0.7);
28132001f49Smrg         draw_object();
28232001f49Smrg      glPopMatrix();
28332001f49Smrg
28432001f49Smrg      if (Locking)
28532001f49Smrg         pthread_mutex_lock(&Mutex);
28632001f49Smrg
28732001f49Smrg      glXSwapBuffers(wt->Dpy, wt->Win);
28832001f49Smrg
28932001f49Smrg      if (Locking)
29032001f49Smrg         pthread_mutex_unlock(&Mutex);
29132001f49Smrg
29232001f49Smrg      if (Animate) {
29332001f49Smrg         usleep(5000);
29432001f49Smrg      }
29532001f49Smrg      else {
29632001f49Smrg         /* wait for signal to draw */
29732001f49Smrg         pthread_mutex_lock(&CondMutex);
29832001f49Smrg         pthread_cond_wait(&CondVar, &CondMutex);
29932001f49Smrg         pthread_mutex_unlock(&CondMutex);
30032001f49Smrg      }
30132001f49Smrg      wt->Angle += 1.0;
30232001f49Smrg   }
30332001f49Smrg}
30432001f49Smrg
30532001f49Smrg
30632001f49Smrgstatic void
30732001f49Smrgkeypress(XEvent *event, struct winthread *wt)
30832001f49Smrg{
30932001f49Smrg   char buf[100];
31032001f49Smrg   KeySym keySym;
31132001f49Smrg   XComposeStatus stat;
31232001f49Smrg
31332001f49Smrg   XLookupString(&event->xkey, buf, sizeof(buf), &keySym, &stat);
31432001f49Smrg
31532001f49Smrg   switch (keySym) {
31632001f49Smrg   case XK_Escape:
31732001f49Smrg      /* tell all threads to exit */
31832001f49Smrg      if (!Animate) {
31932001f49Smrg         signal_redraw();
32032001f49Smrg      }
32132001f49Smrg      ExitFlag = GL_TRUE;
32232001f49Smrg      /*printf("exit draw_loop %d\n", wt->Index);*/
32332001f49Smrg      return;
32432001f49Smrg   case XK_t:
32532001f49Smrg   case XK_T:
32632001f49Smrg      if (Texture) {
32732001f49Smrg         wt->MakeNewTexture = GL_TRUE;
32832001f49Smrg         if (!Animate)
32932001f49Smrg            signal_redraw();
33032001f49Smrg      }
33132001f49Smrg      break;
33232001f49Smrg   case XK_a:
33332001f49Smrg   case XK_A:
33432001f49Smrg      Animate = !Animate;
33532001f49Smrg      if (Animate)  /* yes, prev Animate state! */
33632001f49Smrg         signal_redraw();
33732001f49Smrg      break;
33832001f49Smrg   case XK_s:
33932001f49Smrg   case XK_S:
34032001f49Smrg      if (!Animate)
34132001f49Smrg         signal_redraw();
34232001f49Smrg      break;
34332001f49Smrg   default:
34432001f49Smrg      ; /* nop */
34532001f49Smrg   }
34632001f49Smrg}
34732001f49Smrg
34832001f49Smrg
34932001f49Smrg/*
35032001f49Smrg * The main process thread runs this loop.
35132001f49Smrg * Single display connection for all threads.
35232001f49Smrg */
35332001f49Smrgstatic void
35432001f49Smrgevent_loop(Display *dpy)
35532001f49Smrg{
35632001f49Smrg   XEvent event;
35732001f49Smrg   int i;
35832001f49Smrg
35932001f49Smrg   assert(!MultiDisplays);
36032001f49Smrg
36132001f49Smrg   while (!ExitFlag) {
36232001f49Smrg
36332001f49Smrg      if (Locking) {
36432001f49Smrg         while (1) {
36532001f49Smrg            int k;
36632001f49Smrg            pthread_mutex_lock(&Mutex);
36732001f49Smrg            k = XPending(dpy);
36832001f49Smrg            if (k) {
36932001f49Smrg               XNextEvent(dpy, &event);
37032001f49Smrg               pthread_mutex_unlock(&Mutex);
37132001f49Smrg               break;
37232001f49Smrg            }
37332001f49Smrg            pthread_mutex_unlock(&Mutex);
37432001f49Smrg            usleep(5000);
37532001f49Smrg         }
37632001f49Smrg      }
37732001f49Smrg      else {
37832001f49Smrg         XNextEvent(dpy, &event);
37932001f49Smrg      }
38032001f49Smrg
38132001f49Smrg      switch (event.type) {
38232001f49Smrg         case ConfigureNotify:
38332001f49Smrg            /* Find winthread for this event's window */
38432001f49Smrg            for (i = 0; i < NumWinThreads; i++) {
38532001f49Smrg               struct winthread *wt = &WinThreads[i];
38632001f49Smrg               if (event.xconfigure.window == wt->Win) {
38732001f49Smrg                  resize(wt, event.xconfigure.width,
38832001f49Smrg                         event.xconfigure.height);
38932001f49Smrg                  break;
39032001f49Smrg               }
39132001f49Smrg            }
39232001f49Smrg            break;
39332001f49Smrg         case KeyPress:
39432001f49Smrg            for (i = 0; i < NumWinThreads; i++) {
39532001f49Smrg               struct winthread *wt = &WinThreads[i];
39632001f49Smrg               if (event.xkey.window == wt->Win) {
39732001f49Smrg                  keypress(&event, wt);
39832001f49Smrg                  break;
39932001f49Smrg               }
40032001f49Smrg            }
40132001f49Smrg            break;
40232001f49Smrg         default:
40332001f49Smrg            /*no-op*/ ;
40432001f49Smrg      }
40532001f49Smrg   }
40632001f49Smrg}
40732001f49Smrg
40832001f49Smrg
40932001f49Smrg/*
41032001f49Smrg * Separate display connection for each thread.
41132001f49Smrg */
41232001f49Smrgstatic void
41332001f49Smrgevent_loop_multi(void)
41432001f49Smrg{
41532001f49Smrg   XEvent event;
41632001f49Smrg   int w = 0;
41732001f49Smrg
41832001f49Smrg   assert(MultiDisplays);
41932001f49Smrg
42032001f49Smrg   while (!ExitFlag) {
42132001f49Smrg      struct winthread *wt = &WinThreads[w];
42232001f49Smrg      if (XPending(wt->Dpy)) {
42332001f49Smrg         XNextEvent(wt->Dpy, &event);
42432001f49Smrg         switch (event.type) {
42532001f49Smrg         case ConfigureNotify:
42632001f49Smrg            resize(wt, event.xconfigure.width, event.xconfigure.height);
42732001f49Smrg            break;
42832001f49Smrg         case KeyPress:
42932001f49Smrg            keypress(&event, wt);
43032001f49Smrg            break;
43132001f49Smrg         default:
43232001f49Smrg            ; /* nop */
43332001f49Smrg         }
43432001f49Smrg      }
43532001f49Smrg      w = (w + 1) % NumWinThreads;
43632001f49Smrg      usleep(5000);
43732001f49Smrg   }
43832001f49Smrg}
43932001f49Smrg
44032001f49Smrg
44132001f49Smrg
44232001f49Smrg/*
44332001f49Smrg * we'll call this once for each thread, before the threads are created.
44432001f49Smrg */
44532001f49Smrgstatic void
44632001f49Smrgcreate_window(struct winthread *wt, GLXContext shareCtx)
44732001f49Smrg{
44832001f49Smrg   Window win;
44932001f49Smrg   GLXContext ctx;
45032001f49Smrg   int attrib[] = { GLX_RGBA,
45132001f49Smrg		    GLX_RED_SIZE, 1,
45232001f49Smrg		    GLX_GREEN_SIZE, 1,
45332001f49Smrg		    GLX_BLUE_SIZE, 1,
45432001f49Smrg                    GLX_DEPTH_SIZE, 1,
45532001f49Smrg		    GLX_DOUBLEBUFFER,
45632001f49Smrg		    None };
45732001f49Smrg   int scrnum;
45832001f49Smrg   XSetWindowAttributes attr;
45932001f49Smrg   unsigned long mask;
46032001f49Smrg   Window root;
46132001f49Smrg   XVisualInfo *visinfo;
46232001f49Smrg   int width = 160, height = 160;
46332001f49Smrg   int xpos = (wt->Index % 8) * (width + 10);
46432001f49Smrg   int ypos = (wt->Index / 8) * (width + 20);
46532001f49Smrg
46632001f49Smrg   scrnum = DefaultScreen(wt->Dpy);
46732001f49Smrg   root = RootWindow(wt->Dpy, scrnum);
46832001f49Smrg
46932001f49Smrg   visinfo = glXChooseVisual(wt->Dpy, scrnum, attrib);
47032001f49Smrg   if (!visinfo) {
47132001f49Smrg      Error("Unable to find RGB, Z, double-buffered visual");
47232001f49Smrg   }
47332001f49Smrg
47432001f49Smrg   /* window attributes */
47532001f49Smrg   attr.background_pixel = 0;
47632001f49Smrg   attr.border_pixel = 0;
47732001f49Smrg   attr.colormap = XCreateColormap(wt->Dpy, root, visinfo->visual, AllocNone);
47832001f49Smrg   attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
47932001f49Smrg   mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
48032001f49Smrg
48132001f49Smrg   win = XCreateWindow(wt->Dpy, root, xpos, ypos, width, height,
48232001f49Smrg		        0, visinfo->depth, InputOutput,
48332001f49Smrg		        visinfo->visual, mask, &attr);
48432001f49Smrg   if (!win) {
48532001f49Smrg      Error("Couldn't create window");
48632001f49Smrg   }
48732001f49Smrg
48832001f49Smrg   {
48932001f49Smrg      XSizeHints sizehints;
49032001f49Smrg      sizehints.x = xpos;
49132001f49Smrg      sizehints.y = ypos;
49232001f49Smrg      sizehints.width  = width;
49332001f49Smrg      sizehints.height = height;
49432001f49Smrg      sizehints.flags = USSize | USPosition;
49532001f49Smrg      XSetNormalHints(wt->Dpy, win, &sizehints);
49632001f49Smrg      XSetStandardProperties(wt->Dpy, win, "glthreads", "glthreads",
49732001f49Smrg                              None, (char **)NULL, 0, &sizehints);
49832001f49Smrg   }
49932001f49Smrg
50032001f49Smrg
50132001f49Smrg   ctx = glXCreateContext(wt->Dpy, visinfo, shareCtx, True);
50232001f49Smrg   if (!ctx) {
50332001f49Smrg      Error("Couldn't create GLX context");
50432001f49Smrg   }
50532001f49Smrg
50632001f49Smrg   XMapWindow(wt->Dpy, win);
50732001f49Smrg   XSync(wt->Dpy, 0);
50832001f49Smrg
50932001f49Smrg   /* save the info for this window/context */
51032001f49Smrg   wt->Win = win;
51132001f49Smrg   wt->Context = ctx;
51232001f49Smrg   wt->Angle = 0.0;
51332001f49Smrg   wt->WinWidth = width;
51432001f49Smrg   wt->WinHeight = height;
51532001f49Smrg   wt->NewSize = GL_TRUE;
51632001f49Smrg}
51732001f49Smrg
51832001f49Smrg
51932001f49Smrg/*
52032001f49Smrg * Called by pthread_create()
52132001f49Smrg */
52232001f49Smrgstatic void *
52332001f49Smrgthread_function(void *p)
52432001f49Smrg{
52532001f49Smrg   struct winthread *wt = (struct winthread *) p;
52632001f49Smrg   draw_loop(wt);
52732001f49Smrg   return NULL;
52832001f49Smrg}
52932001f49Smrg
53032001f49Smrg
53132001f49Smrg/*
53232001f49Smrg * called before exit to wait for all threads to finish
53332001f49Smrg */
53432001f49Smrgstatic void
53532001f49Smrgclean_up(void)
53632001f49Smrg{
53732001f49Smrg   int i;
53832001f49Smrg
53932001f49Smrg   /* wait for threads to finish */
54032001f49Smrg   for (i = 0; i < NumWinThreads; i++) {
54132001f49Smrg      pthread_join(WinThreads[i].Thread, NULL);
54232001f49Smrg   }
54332001f49Smrg
54432001f49Smrg   for (i = 0; i < NumWinThreads; i++) {
54532001f49Smrg      glXDestroyContext(WinThreads[i].Dpy, WinThreads[i].Context);
54632001f49Smrg      XDestroyWindow(WinThreads[i].Dpy, WinThreads[i].Win);
54732001f49Smrg   }
54832001f49Smrg}
54932001f49Smrg
55032001f49Smrg
55132001f49Smrgstatic void
55232001f49Smrgusage(void)
55332001f49Smrg{
55432001f49Smrg   printf("glthreads: test of GL thread safety (any key = exit)\n");
55532001f49Smrg   printf("Usage:\n");
55632001f49Smrg   printf("  glthreads [options]\n");
55732001f49Smrg   printf("Options:\n");
55832001f49Smrg   printf("   -display DISPLAYNAME  Specify display string\n");
55932001f49Smrg   printf("   -n NUMTHREADS  Number of threads to create\n");
56032001f49Smrg   printf("   -p  Use a separate display connection for each thread\n");
56132001f49Smrg   printf("   -l  Use application-side locking\n");
56232001f49Smrg   printf("   -t  Enable texturing\n");
56332001f49Smrg   printf("Keyboard:\n");
56432001f49Smrg   printf("   Esc  Exit\n");
56532001f49Smrg   printf("   t    Change texture image (requires -t option)\n");
56632001f49Smrg   printf("   a    Toggle animation\n");
56732001f49Smrg   printf("   s    Step rotation (when not animating)\n");
56832001f49Smrg}
56932001f49Smrg
57032001f49Smrg
57132001f49Smrgint
57232001f49Smrgmain(int argc, char *argv[])
57332001f49Smrg{
57432001f49Smrg   char *displayName = NULL;
57532001f49Smrg   int numThreads = 2;
57632001f49Smrg   Display *dpy = NULL;
57732001f49Smrg   int i;
57832001f49Smrg   Status threadStat;
57932001f49Smrg
58032001f49Smrg   if (argc == 1) {
58132001f49Smrg      usage();
58232001f49Smrg   }
58332001f49Smrg   else {
58432001f49Smrg      int i;
58532001f49Smrg      for (i = 1; i < argc; i++) {
58632001f49Smrg         if (strcmp(argv[i], "-display") == 0 && i + 1 < argc) {
58732001f49Smrg            displayName = argv[i + 1];
58832001f49Smrg            i++;
58932001f49Smrg         }
59032001f49Smrg         else if (strcmp(argv[i], "-p") == 0) {
59132001f49Smrg            MultiDisplays = 1;
59232001f49Smrg         }
59332001f49Smrg         else if (strcmp(argv[i], "-l") == 0) {
59432001f49Smrg            Locking = 1;
59532001f49Smrg         }
59632001f49Smrg         else if (strcmp(argv[i], "-t") == 0) {
59732001f49Smrg            Texture = 1;
59832001f49Smrg         }
59932001f49Smrg         else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) {
60032001f49Smrg            numThreads = atoi(argv[i + 1]);
60132001f49Smrg            if (numThreads < 1)
60232001f49Smrg               numThreads = 1;
60332001f49Smrg            else if (numThreads > MAX_WINTHREADS)
60432001f49Smrg               numThreads = MAX_WINTHREADS;
60532001f49Smrg            i++;
60632001f49Smrg         }
60732001f49Smrg         else {
60832001f49Smrg            usage();
60932001f49Smrg            exit(1);
61032001f49Smrg         }
61132001f49Smrg      }
61232001f49Smrg   }
61332001f49Smrg
61432001f49Smrg   if (Locking)
61532001f49Smrg      printf("glthreads: Using explicit locks around Xlib calls.\n");
61632001f49Smrg   else
61732001f49Smrg      printf("glthreads: No explict locking.\n");
61832001f49Smrg
61932001f49Smrg   if (MultiDisplays)
62032001f49Smrg      printf("glthreads: Per-thread display connections.\n");
62132001f49Smrg   else
62232001f49Smrg      printf("glthreads: Single display connection.\n");
62332001f49Smrg
62432001f49Smrg   /*
62532001f49Smrg    * VERY IMPORTANT: call XInitThreads() before any other Xlib functions.
62632001f49Smrg    */
62732001f49Smrg   if (!MultiDisplays) {
62832001f49Smrg      if (!Locking) {
62932001f49Smrg         threadStat = XInitThreads();
63032001f49Smrg      if (threadStat) {
63132001f49Smrg         printf("XInitThreads() returned %d (success)\n", (int) threadStat);
63232001f49Smrg      }
63332001f49Smrg      else {
63432001f49Smrg         printf("XInitThreads() returned 0 (failure- this program may fail)\n");
63532001f49Smrg      }
63632001f49Smrg      }
63732001f49Smrg
63832001f49Smrg      dpy = XOpenDisplay(displayName);
63932001f49Smrg      if (!dpy) {
64032001f49Smrg         fprintf(stderr, "Unable to open display %s\n", XDisplayName(displayName));
64132001f49Smrg         return -1;
64232001f49Smrg      }
64332001f49Smrg   }
64432001f49Smrg
64532001f49Smrg   pthread_mutex_init(&Mutex, NULL);
64632001f49Smrg   pthread_mutex_init(&CondMutex, NULL);
64732001f49Smrg   pthread_cond_init(&CondVar, NULL);
64832001f49Smrg
64932001f49Smrg   printf("glthreads: creating windows\n");
65032001f49Smrg
65132001f49Smrg   NumWinThreads = numThreads;
65232001f49Smrg
65332001f49Smrg   /* Create the GLX windows and contexts */
65432001f49Smrg   for (i = 0; i < numThreads; i++) {
65532001f49Smrg      GLXContext share;
65632001f49Smrg
65732001f49Smrg      if (MultiDisplays) {
65832001f49Smrg         WinThreads[i].Dpy = XOpenDisplay(displayName);
65932001f49Smrg         assert(WinThreads[i].Dpy);
66032001f49Smrg      }
66132001f49Smrg      else {
66232001f49Smrg         WinThreads[i].Dpy = dpy;
66332001f49Smrg      }
66432001f49Smrg      WinThreads[i].Index = i;
66532001f49Smrg      WinThreads[i].Initialized = GL_FALSE;
66632001f49Smrg
66732001f49Smrg      share = (Texture && i > 0) ? WinThreads[0].Context : 0;
66832001f49Smrg
66932001f49Smrg      create_window(&WinThreads[i], share);
67032001f49Smrg   }
67132001f49Smrg
67232001f49Smrg   printf("glthreads: creating threads\n");
67332001f49Smrg
67432001f49Smrg   /* Create the threads */
67532001f49Smrg   for (i = 0; i < numThreads; i++) {
67632001f49Smrg      pthread_create(&WinThreads[i].Thread, NULL, thread_function,
67732001f49Smrg                     (void*) &WinThreads[i]);
67832001f49Smrg      printf("glthreads: Created thread %p\n", (void *) WinThreads[i].Thread);
67932001f49Smrg   }
68032001f49Smrg
68132001f49Smrg   if (MultiDisplays)
68232001f49Smrg      event_loop_multi();
68332001f49Smrg   else
68432001f49Smrg      event_loop(dpy);
68532001f49Smrg
68632001f49Smrg   clean_up();
68732001f49Smrg
68832001f49Smrg   if (MultiDisplays) {
68932001f49Smrg      for (i = 0; i < numThreads; i++) {
69032001f49Smrg         XCloseDisplay(WinThreads[i].Dpy);
69132001f49Smrg      }
69232001f49Smrg   }
69332001f49Smrg   else {
69432001f49Smrg      XCloseDisplay(dpy);
69532001f49Smrg   }
69632001f49Smrg
69732001f49Smrg   return 0;
69832001f49Smrg}
699