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 * Ported to EGL by Chia-I Wu <olvaffe@gmail.com>
2232001f49Smrg */
2332001f49Smrg
2432001f49Smrg
2532001f49Smrg/*
2632001f49Smrg * This program tests EGL thread safety.
2732001f49Smrg * Command line options:
2832001f49Smrg *  -p                       Open a display connection for each thread
2932001f49Smrg *  -l                       Enable application-side locking
3032001f49Smrg *  -n <num threads>         Number of threads to create (default is 2)
3132001f49Smrg *  -display <display name>  Specify X display (default is $DISPLAY)
3232001f49Smrg *  -t                       Use texture mapping
3332001f49Smrg *
3432001f49Smrg * Brian Paul  20 July 2000
3532001f49Smrg */
3632001f49Smrg
3732001f49Smrg
3832001f49Smrg/*
3932001f49Smrg * Notes:
4032001f49Smrg * - Each thread gets its own EGL context.
4132001f49Smrg *
4232001f49Smrg * - The EGL contexts share texture objects.
4332001f49Smrg *
4432001f49Smrg * - When 't' is pressed to update the texture image, the window/thread which
4532001f49Smrg *   has input focus is signalled to change the texture.  The other threads
4632001f49Smrg *   should see the updated texture the next time they call glBindTexture.
4732001f49Smrg */
4832001f49Smrg
4932001f49Smrg
5032001f49Smrg#if defined(PTHREADS)   /* defined by Mesa on Linux and other platforms */
5132001f49Smrg
5232001f49Smrg#include <assert.h>
5332001f49Smrg#include <X11/Xlib.h>
5432001f49Smrg#include <X11/Xutil.h>
5532001f49Smrg#include "gl_wrap.h"
5632001f49Smrg#include <EGL/egl.h>
5732001f49Smrg#include <math.h>
5832001f49Smrg#include <stdio.h>
5932001f49Smrg#include <stdlib.h>
6032001f49Smrg#include <string.h>
6132001f49Smrg#include <unistd.h>
6232001f49Smrg#include <pthread.h>
6332001f49Smrg
6432001f49Smrg
6532001f49Smrg/*
6632001f49Smrg * Each window/thread/context:
6732001f49Smrg */
6832001f49Smrgstruct winthread {
6932001f49Smrg   Display *Dpy;
7032001f49Smrg   int Index;
7132001f49Smrg   pthread_t Thread;
7232001f49Smrg   Window Win;
7332001f49Smrg   EGLDisplay Display;
7432001f49Smrg   EGLContext Context;
7532001f49Smrg   EGLSurface Surface;
7632001f49Smrg   float Angle;
7732001f49Smrg   int WinWidth, WinHeight;
7832001f49Smrg   GLboolean NewSize;
7932001f49Smrg   GLboolean Initialized;
8032001f49Smrg   GLboolean MakeNewTexture;
8132001f49Smrg};
8232001f49Smrg
8332001f49Smrg
8432001f49Smrg#define MAX_WINTHREADS 100
8532001f49Smrgstatic struct winthread WinThreads[MAX_WINTHREADS];
8632001f49Smrgstatic int NumWinThreads = 0;
8732001f49Smrgstatic volatile GLboolean ExitFlag = GL_FALSE;
8832001f49Smrg
8932001f49Smrgstatic GLboolean MultiDisplays = 0;
9032001f49Smrgstatic GLboolean Locking = 0;
9132001f49Smrgstatic GLboolean Texture = GL_FALSE;
9232001f49Smrgstatic GLuint TexObj = 12;
9332001f49Smrgstatic GLboolean Animate = GL_TRUE;
9432001f49Smrg
9532001f49Smrgstatic pthread_mutex_t Mutex;
9632001f49Smrgstatic pthread_cond_t CondVar;
9732001f49Smrgstatic pthread_mutex_t CondMutex;
9832001f49Smrg
9932001f49Smrg
10032001f49Smrgstatic void
10132001f49SmrgError(const char *msg)
10232001f49Smrg{
10332001f49Smrg   fprintf(stderr, "Error: %s\n", msg);
10432001f49Smrg   exit(1);
10532001f49Smrg}
10632001f49Smrg
10732001f49Smrg
10832001f49Smrgstatic void
10932001f49Smrgsignal_redraw(void)
11032001f49Smrg{
11132001f49Smrg   pthread_mutex_lock(&CondMutex);
11232001f49Smrg   pthread_cond_broadcast(&CondVar);
11332001f49Smrg   pthread_mutex_unlock(&CondMutex);
11432001f49Smrg}
11532001f49Smrg
11632001f49Smrg
11732001f49Smrgstatic void
11832001f49SmrgMakeNewTexture(struct winthread *wt)
11932001f49Smrg{
12032001f49Smrg#define TEX_SIZE 128
12132001f49Smrg   static float step = 0.0;
12232001f49Smrg   GLfloat image[TEX_SIZE][TEX_SIZE][4];
12332001f49Smrg   GLint width;
12432001f49Smrg   int i, j;
12532001f49Smrg
12632001f49Smrg   for (j = 0; j < TEX_SIZE; j++) {
12732001f49Smrg      for (i = 0; i < TEX_SIZE; i++) {
12832001f49Smrg         float dt = 5.0 * (j - 0.5 * TEX_SIZE) / TEX_SIZE;
12932001f49Smrg         float ds = 5.0 * (i - 0.5 * TEX_SIZE) / TEX_SIZE;
13032001f49Smrg         float r = dt * dt + ds * ds + step;
13132001f49Smrg         image[j][i][0] =
13232001f49Smrg         image[j][i][1] =
13332001f49Smrg         image[j][i][2] = 0.75 + 0.25 * cos(r);
13432001f49Smrg         image[j][i][3] = 1.0;
13532001f49Smrg      }
13632001f49Smrg   }
13732001f49Smrg
13832001f49Smrg   step += 0.5;
13932001f49Smrg
14032001f49Smrg   glBindTexture(GL_TEXTURE_2D, TexObj);
14132001f49Smrg
14232001f49Smrg   glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
14332001f49Smrg   if (width) {
14432001f49Smrg      assert(width == TEX_SIZE);
14532001f49Smrg      /* sub-tex replace */
14632001f49Smrg      glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, TEX_SIZE, TEX_SIZE,
14732001f49Smrg                   GL_RGBA, GL_FLOAT, image);
14832001f49Smrg   }
14932001f49Smrg   else {
15032001f49Smrg      /* create new */
15132001f49Smrg      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
15232001f49Smrg      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
15332001f49Smrg
15432001f49Smrg      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEX_SIZE, TEX_SIZE, 0,
15532001f49Smrg                   GL_RGBA, GL_FLOAT, image);
15632001f49Smrg   }
15732001f49Smrg}
15832001f49Smrg
15932001f49Smrg
16032001f49Smrg
16132001f49Smrg/* draw a colored cube */
16232001f49Smrgstatic void
16332001f49Smrgdraw_object(void)
16432001f49Smrg{
16532001f49Smrg   glPushMatrix();
16632001f49Smrg   glScalef(0.75, 0.75, 0.75);
16732001f49Smrg
16832001f49Smrg   glColor3f(1, 0, 0);
16932001f49Smrg
17032001f49Smrg   if (Texture) {
17132001f49Smrg      glBindTexture(GL_TEXTURE_2D, TexObj);
17232001f49Smrg      glEnable(GL_TEXTURE_2D);
17332001f49Smrg   }
17432001f49Smrg   else {
17532001f49Smrg      glDisable(GL_TEXTURE_2D);
17632001f49Smrg   }
17732001f49Smrg
17832001f49Smrg   glBegin(GL_QUADS);
17932001f49Smrg
18032001f49Smrg   /* -X */
18132001f49Smrg   glColor3f(0, 1, 1);
18232001f49Smrg   glTexCoord2f(0, 0);  glVertex3f(-1, -1, -1);
18332001f49Smrg   glTexCoord2f(1, 0);  glVertex3f(-1,  1, -1);
18432001f49Smrg   glTexCoord2f(1, 1);  glVertex3f(-1,  1,  1);
18532001f49Smrg   glTexCoord2f(0, 1);  glVertex3f(-1, -1,  1);
18632001f49Smrg
18732001f49Smrg   /* +X */
18832001f49Smrg   glColor3f(1, 0, 0);
18932001f49Smrg   glTexCoord2f(0, 0);  glVertex3f(1, -1, -1);
19032001f49Smrg   glTexCoord2f(1, 0);  glVertex3f(1,  1, -1);
19132001f49Smrg   glTexCoord2f(1, 1);  glVertex3f(1,  1,  1);
19232001f49Smrg   glTexCoord2f(0, 1);  glVertex3f(1, -1,  1);
19332001f49Smrg
19432001f49Smrg   /* -Y */
19532001f49Smrg   glColor3f(1, 0, 1);
19632001f49Smrg   glTexCoord2f(0, 0);  glVertex3f(-1, -1, -1);
19732001f49Smrg   glTexCoord2f(1, 0);  glVertex3f( 1, -1, -1);
19832001f49Smrg   glTexCoord2f(1, 1);  glVertex3f( 1, -1,  1);
19932001f49Smrg   glTexCoord2f(0, 1);  glVertex3f(-1, -1,  1);
20032001f49Smrg
20132001f49Smrg   /* +Y */
20232001f49Smrg   glColor3f(0, 1, 0);
20332001f49Smrg   glTexCoord2f(0, 0);  glVertex3f(-1, 1, -1);
20432001f49Smrg   glTexCoord2f(1, 0);  glVertex3f( 1, 1, -1);
20532001f49Smrg   glTexCoord2f(1, 1);  glVertex3f( 1, 1,  1);
20632001f49Smrg   glTexCoord2f(0, 1);  glVertex3f(-1, 1,  1);
20732001f49Smrg
20832001f49Smrg   /* -Z */
20932001f49Smrg   glColor3f(1, 1, 0);
21032001f49Smrg   glTexCoord2f(0, 0);  glVertex3f(-1, -1, -1);
21132001f49Smrg   glTexCoord2f(1, 0);  glVertex3f( 1, -1, -1);
21232001f49Smrg   glTexCoord2f(1, 1);  glVertex3f( 1,  1, -1);
21332001f49Smrg   glTexCoord2f(0, 1);  glVertex3f(-1,  1, -1);
21432001f49Smrg
21532001f49Smrg   /* +Y */
21632001f49Smrg   glColor3f(0, 0, 1);
21732001f49Smrg   glTexCoord2f(0, 0);  glVertex3f(-1, -1, 1);
21832001f49Smrg   glTexCoord2f(1, 0);  glVertex3f( 1, -1, 1);
21932001f49Smrg   glTexCoord2f(1, 1);  glVertex3f( 1,  1, 1);
22032001f49Smrg   glTexCoord2f(0, 1);  glVertex3f(-1,  1, 1);
22132001f49Smrg
22232001f49Smrg   glEnd();
22332001f49Smrg
22432001f49Smrg   glPopMatrix();
22532001f49Smrg}
22632001f49Smrg
22732001f49Smrg
22832001f49Smrg/* signal resize of given window */
22932001f49Smrgstatic void
23032001f49Smrgresize(struct winthread *wt, int w, int h)
23132001f49Smrg{
23232001f49Smrg   wt->NewSize = GL_TRUE;
23332001f49Smrg   wt->WinWidth = w;
23432001f49Smrg   wt->WinHeight = h;
23532001f49Smrg   if (!Animate)
23632001f49Smrg      signal_redraw();
23732001f49Smrg}
23832001f49Smrg
23932001f49Smrg
24032001f49Smrg/*
24132001f49Smrg * We have an instance of this for each thread.
24232001f49Smrg */
24332001f49Smrgstatic void
24432001f49Smrgdraw_loop(struct winthread *wt)
24532001f49Smrg{
24632001f49Smrg   while (!ExitFlag) {
24732001f49Smrg
24832001f49Smrg      if (Locking)
24932001f49Smrg         pthread_mutex_lock(&Mutex);
25032001f49Smrg
25132001f49Smrg      if (!wt->Initialized) {
25232001f49Smrg         eglMakeCurrent(wt->Display, wt->Surface, wt->Surface, wt->Context);
25332001f49Smrg         printf("xeglthreads: %d: GL_RENDERER = %s\n", wt->Index,
25432001f49Smrg                (char *) glGetString(GL_RENDERER));
25532001f49Smrg         if (Texture /*&& wt->Index == 0*/) {
25632001f49Smrg            MakeNewTexture(wt);
25732001f49Smrg         }
25832001f49Smrg         wt->Initialized = GL_TRUE;
25932001f49Smrg      }
26032001f49Smrg
26132001f49Smrg      if (Locking)
26232001f49Smrg         pthread_mutex_unlock(&Mutex);
26332001f49Smrg
26432001f49Smrg      eglBindAPI(EGL_OPENGL_API);
26532001f49Smrg      if (eglGetCurrentContext() != wt->Context) {
26632001f49Smrg         printf("xeglthreads: current context %p != %p\n",
26732001f49Smrg               eglGetCurrentContext(), wt->Context);
26832001f49Smrg      }
26932001f49Smrg
27032001f49Smrg      glEnable(GL_DEPTH_TEST);
27132001f49Smrg
27232001f49Smrg      if (wt->NewSize) {
27332001f49Smrg         GLfloat w = (float) wt->WinWidth / (float) wt->WinHeight;
27432001f49Smrg         glViewport(0, 0, wt->WinWidth, wt->WinHeight);
27532001f49Smrg         glMatrixMode(GL_PROJECTION);
27632001f49Smrg         glLoadIdentity();
27732001f49Smrg         glFrustum(-w, w, -1.0, 1.0, 1.5, 10);
27832001f49Smrg         glMatrixMode(GL_MODELVIEW);
27932001f49Smrg         glLoadIdentity();
28032001f49Smrg         glTranslatef(0, 0, -2.5);
28132001f49Smrg         wt->NewSize = GL_FALSE;
28232001f49Smrg      }
28332001f49Smrg
28432001f49Smrg      if (wt->MakeNewTexture) {
28532001f49Smrg         MakeNewTexture(wt);
28632001f49Smrg         wt->MakeNewTexture = GL_FALSE;
28732001f49Smrg      }
28832001f49Smrg
28932001f49Smrg      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
29032001f49Smrg
29132001f49Smrg      glPushMatrix();
29232001f49Smrg         glRotatef(wt->Angle, 0, 1, 0);
29332001f49Smrg         glRotatef(wt->Angle, 1, 0, 0);
29432001f49Smrg         glScalef(0.7, 0.7, 0.7);
29532001f49Smrg         draw_object();
29632001f49Smrg      glPopMatrix();
29732001f49Smrg
29832001f49Smrg      if (Locking)
29932001f49Smrg         pthread_mutex_lock(&Mutex);
30032001f49Smrg
30132001f49Smrg      eglSwapBuffers(wt->Display, wt->Surface);
30232001f49Smrg
30332001f49Smrg      if (Locking)
30432001f49Smrg         pthread_mutex_unlock(&Mutex);
30532001f49Smrg
30632001f49Smrg      if (Animate) {
30732001f49Smrg         usleep(5000);
30832001f49Smrg      }
30932001f49Smrg      else {
31032001f49Smrg         /* wait for signal to draw */
31132001f49Smrg         pthread_mutex_lock(&CondMutex);
31232001f49Smrg         pthread_cond_wait(&CondVar, &CondMutex);
31332001f49Smrg         pthread_mutex_unlock(&CondMutex);
31432001f49Smrg      }
31532001f49Smrg      wt->Angle += 1.0;
31632001f49Smrg   }
31732001f49Smrg   eglMakeCurrent(wt->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
31832001f49Smrg}
31932001f49Smrg
32032001f49Smrg
32132001f49Smrgstatic void
32232001f49Smrgkeypress(XEvent *event, struct winthread *wt)
32332001f49Smrg{
32432001f49Smrg   char buf[100];
32532001f49Smrg   KeySym keySym;
32632001f49Smrg   XComposeStatus stat;
32732001f49Smrg
32832001f49Smrg   XLookupString(&event->xkey, buf, sizeof(buf), &keySym, &stat);
32932001f49Smrg
33032001f49Smrg   switch (keySym) {
33132001f49Smrg   case XK_Escape:
33232001f49Smrg      /* tell all threads to exit */
33332001f49Smrg      if (!Animate) {
33432001f49Smrg         signal_redraw();
33532001f49Smrg      }
33632001f49Smrg      ExitFlag = GL_TRUE;
33732001f49Smrg      /*printf("exit draw_loop %d\n", wt->Index);*/
33832001f49Smrg      return;
33932001f49Smrg   case XK_t:
34032001f49Smrg   case XK_T:
34132001f49Smrg      if (Texture) {
34232001f49Smrg         wt->MakeNewTexture = GL_TRUE;
34332001f49Smrg         if (!Animate)
34432001f49Smrg            signal_redraw();
34532001f49Smrg      }
34632001f49Smrg      break;
34732001f49Smrg   case XK_a:
34832001f49Smrg   case XK_A:
34932001f49Smrg      Animate = !Animate;
35032001f49Smrg      if (Animate)  /* yes, prev Animate state! */
35132001f49Smrg         signal_redraw();
35232001f49Smrg      break;
35332001f49Smrg   case XK_s:
35432001f49Smrg   case XK_S:
35532001f49Smrg      if (!Animate)
35632001f49Smrg         signal_redraw();
35732001f49Smrg      break;
35832001f49Smrg   default:
35932001f49Smrg      ; /* nop */
36032001f49Smrg   }
36132001f49Smrg}
36232001f49Smrg
36332001f49Smrg
36432001f49Smrg/*
36532001f49Smrg * The main process thread runs this loop.
36632001f49Smrg * Single display connection for all threads.
36732001f49Smrg */
36832001f49Smrgstatic void
36932001f49Smrgevent_loop(Display *dpy)
37032001f49Smrg{
37132001f49Smrg   XEvent event;
37232001f49Smrg   int i;
37332001f49Smrg
37432001f49Smrg   assert(!MultiDisplays);
37532001f49Smrg
37632001f49Smrg   while (!ExitFlag) {
37732001f49Smrg
37832001f49Smrg      if (Locking) {
37932001f49Smrg         while (1) {
38032001f49Smrg            int k;
38132001f49Smrg            pthread_mutex_lock(&Mutex);
38232001f49Smrg            k = XPending(dpy);
38332001f49Smrg            if (k) {
38432001f49Smrg               XNextEvent(dpy, &event);
38532001f49Smrg               pthread_mutex_unlock(&Mutex);
38632001f49Smrg               break;
38732001f49Smrg            }
38832001f49Smrg            pthread_mutex_unlock(&Mutex);
38932001f49Smrg            usleep(5000);
39032001f49Smrg         }
39132001f49Smrg      }
39232001f49Smrg      else {
39332001f49Smrg         XNextEvent(dpy, &event);
39432001f49Smrg      }
39532001f49Smrg
39632001f49Smrg      switch (event.type) {
39732001f49Smrg         case ConfigureNotify:
39832001f49Smrg            /* Find winthread for this event's window */
39932001f49Smrg            for (i = 0; i < NumWinThreads; i++) {
40032001f49Smrg               struct winthread *wt = &WinThreads[i];
40132001f49Smrg               if (event.xconfigure.window == wt->Win) {
40232001f49Smrg                  resize(wt, event.xconfigure.width,
40332001f49Smrg                         event.xconfigure.height);
40432001f49Smrg                  break;
40532001f49Smrg               }
40632001f49Smrg            }
40732001f49Smrg            break;
40832001f49Smrg         case KeyPress:
40932001f49Smrg            for (i = 0; i < NumWinThreads; i++) {
41032001f49Smrg               struct winthread *wt = &WinThreads[i];
41132001f49Smrg               if (event.xkey.window == wt->Win) {
41232001f49Smrg                  keypress(&event, wt);
41332001f49Smrg                  break;
41432001f49Smrg               }
41532001f49Smrg            }
41632001f49Smrg            break;
41732001f49Smrg         default:
41832001f49Smrg            /*no-op*/ ;
41932001f49Smrg      }
42032001f49Smrg   }
42132001f49Smrg}
42232001f49Smrg
42332001f49Smrg
42432001f49Smrg/*
42532001f49Smrg * Separate display connection for each thread.
42632001f49Smrg */
42732001f49Smrgstatic void
42832001f49Smrgevent_loop_multi(void)
42932001f49Smrg{
43032001f49Smrg   XEvent event;
43132001f49Smrg   int w = 0;
43232001f49Smrg
43332001f49Smrg   assert(MultiDisplays);
43432001f49Smrg
43532001f49Smrg   while (!ExitFlag) {
43632001f49Smrg      struct winthread *wt = &WinThreads[w];
43732001f49Smrg      if (XPending(wt->Dpy)) {
43832001f49Smrg         XNextEvent(wt->Dpy, &event);
43932001f49Smrg         switch (event.type) {
44032001f49Smrg         case ConfigureNotify:
44132001f49Smrg            resize(wt, event.xconfigure.width, event.xconfigure.height);
44232001f49Smrg            break;
44332001f49Smrg         case KeyPress:
44432001f49Smrg            keypress(&event, wt);
44532001f49Smrg            break;
44632001f49Smrg         default:
44732001f49Smrg            ; /* nop */
44832001f49Smrg         }
44932001f49Smrg      }
45032001f49Smrg      w = (w + 1) % NumWinThreads;
45132001f49Smrg      usleep(5000);
45232001f49Smrg   }
45332001f49Smrg}
45432001f49Smrg
45532001f49Smrg
45632001f49Smrg
45732001f49Smrg/*
45832001f49Smrg * we'll call this once for each thread, before the threads are created.
45932001f49Smrg */
46032001f49Smrgstatic void
46132001f49Smrgcreate_window(struct winthread *wt, EGLContext shareCtx)
46232001f49Smrg{
46332001f49Smrg   Window win;
46432001f49Smrg   EGLContext ctx;
46532001f49Smrg   EGLSurface surf;
46632001f49Smrg   EGLint attribs[] = { EGL_RED_SIZE, 1,
46732001f49Smrg                        EGL_GREEN_SIZE, 1,
46832001f49Smrg                        EGL_BLUE_SIZE, 1,
46932001f49Smrg                        EGL_DEPTH_SIZE, 1,
47032001f49Smrg                        EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
47132001f49Smrg                        EGL_NONE };
47232001f49Smrg   EGLConfig config;
47332001f49Smrg   EGLint num_configs;
47432001f49Smrg   EGLint vid;
47532001f49Smrg   int scrnum;
47632001f49Smrg   XSetWindowAttributes attr;
47732001f49Smrg   unsigned long mask;
47832001f49Smrg   Window root;
47932001f49Smrg   XVisualInfo *visinfo, visTemplate;
48032001f49Smrg   int num_visuals;
48132001f49Smrg   int width = 160, height = 160;
48232001f49Smrg   int xpos = (wt->Index % 8) * (width + 10);
48332001f49Smrg   int ypos = (wt->Index / 8) * (width + 20);
48432001f49Smrg
48532001f49Smrg   scrnum = DefaultScreen(wt->Dpy);
48632001f49Smrg   root = RootWindow(wt->Dpy, scrnum);
48732001f49Smrg
48832001f49Smrg   if (!eglChooseConfig(wt->Display, attribs, &config, 1, &num_configs) ||
48932001f49Smrg       !num_configs) {
49032001f49Smrg      Error("Unable to choose an EGL config");
49132001f49Smrg   }
49232001f49Smrg
49332001f49Smrg   assert(config);
49432001f49Smrg   assert(num_configs > 0);
49532001f49Smrg
49632001f49Smrg   if (!eglGetConfigAttrib(wt->Display, config, EGL_NATIVE_VISUAL_ID, &vid)) {
49732001f49Smrg      Error("Unable to get visual id of EGL config\n");
49832001f49Smrg   }
49932001f49Smrg
50032001f49Smrg   visTemplate.visualid = vid;
50132001f49Smrg   visinfo = XGetVisualInfo(wt->Dpy, VisualIDMask,
50232001f49Smrg                        &visTemplate, &num_visuals);
50332001f49Smrg   if (!visinfo) {
50432001f49Smrg      Error("Unable to find RGB, Z, double-buffered visual");
50532001f49Smrg   }
50632001f49Smrg
50732001f49Smrg   /* window attributes */
50832001f49Smrg   attr.background_pixel = 0;
50932001f49Smrg   attr.border_pixel = 0;
51032001f49Smrg   attr.colormap = XCreateColormap(wt->Dpy, root, visinfo->visual, AllocNone);
51132001f49Smrg   attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
51232001f49Smrg   mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
51332001f49Smrg
51432001f49Smrg   win = XCreateWindow(wt->Dpy, root, xpos, ypos, width, height,
51532001f49Smrg                        0, visinfo->depth, InputOutput,
51632001f49Smrg                        visinfo->visual, mask, &attr);
51732001f49Smrg   if (!win) {
51832001f49Smrg      Error("Couldn't create window");
51932001f49Smrg   }
52032001f49Smrg
52132001f49Smrg   XFree(visinfo);
52232001f49Smrg
52332001f49Smrg   {
52432001f49Smrg      XSizeHints sizehints;
52532001f49Smrg      sizehints.x = xpos;
52632001f49Smrg      sizehints.y = ypos;
52732001f49Smrg      sizehints.width  = width;
52832001f49Smrg      sizehints.height = height;
52932001f49Smrg      sizehints.flags = USSize | USPosition;
53032001f49Smrg      XSetNormalHints(wt->Dpy, win, &sizehints);
53132001f49Smrg      XSetStandardProperties(wt->Dpy, win, "xeglthreads", "xeglthreads",
53232001f49Smrg                              None, (char **)NULL, 0, &sizehints);
53332001f49Smrg   }
53432001f49Smrg
53532001f49Smrg   eglBindAPI(EGL_OPENGL_API);
53632001f49Smrg
53732001f49Smrg   ctx = eglCreateContext(wt->Display, config, shareCtx, NULL);
53832001f49Smrg   if (!ctx) {
53932001f49Smrg      Error("Couldn't create EGL context");
54032001f49Smrg   }
54132001f49Smrg   surf = eglCreateWindowSurface(wt->Display, config, win, NULL);
54232001f49Smrg   if (!surf) {
54332001f49Smrg      Error("Couldn't create EGL surface");
54432001f49Smrg   }
54532001f49Smrg
54632001f49Smrg   XMapWindow(wt->Dpy, win);
54732001f49Smrg   XSync(wt->Dpy, 0);
54832001f49Smrg
54932001f49Smrg   /* save the info for this window/context */
55032001f49Smrg   wt->Win = win;
55132001f49Smrg   wt->Context = ctx;
55232001f49Smrg   wt->Surface = surf;
55332001f49Smrg   wt->Angle = 0.0;
55432001f49Smrg   wt->WinWidth = width;
55532001f49Smrg   wt->WinHeight = height;
55632001f49Smrg   wt->NewSize = GL_TRUE;
55732001f49Smrg}
55832001f49Smrg
55932001f49Smrg
56032001f49Smrg/*
56132001f49Smrg * Called by pthread_create()
56232001f49Smrg */
56332001f49Smrgstatic void *
56432001f49Smrgthread_function(void *p)
56532001f49Smrg{
56632001f49Smrg   struct winthread *wt = (struct winthread *) p;
56732001f49Smrg   draw_loop(wt);
56832001f49Smrg   return NULL;
56932001f49Smrg}
57032001f49Smrg
57132001f49Smrg
57232001f49Smrg/*
57332001f49Smrg * called before exit to wait for all threads to finish
57432001f49Smrg */
57532001f49Smrgstatic void
57632001f49Smrgclean_up(void)
57732001f49Smrg{
57832001f49Smrg   int i;
57932001f49Smrg
58032001f49Smrg   /* wait for threads to finish */
58132001f49Smrg   for (i = 0; i < NumWinThreads; i++) {
58232001f49Smrg      pthread_join(WinThreads[i].Thread, NULL);
58332001f49Smrg   }
58432001f49Smrg
58532001f49Smrg   for (i = 0; i < NumWinThreads; i++) {
58632001f49Smrg      eglDestroyContext(WinThreads[i].Display, WinThreads[i].Context);
58732001f49Smrg      XDestroyWindow(WinThreads[i].Dpy, WinThreads[i].Win);
58832001f49Smrg   }
58932001f49Smrg}
59032001f49Smrg
59132001f49Smrg
59232001f49Smrgstatic void
59332001f49Smrgusage(void)
59432001f49Smrg{
59532001f49Smrg   printf("xeglthreads: test of EGL/GL thread safety (any key = exit)\n");
59632001f49Smrg   printf("Usage:\n");
59732001f49Smrg   printf("  xeglthreads [options]\n");
59832001f49Smrg   printf("Options:\n");
59932001f49Smrg   printf("   -display DISPLAYNAME  Specify display string\n");
60032001f49Smrg   printf("   -n NUMTHREADS  Number of threads to create\n");
60132001f49Smrg   printf("   -p  Use a separate display connection for each thread\n");
60232001f49Smrg   printf("   -l  Use application-side locking\n");
60332001f49Smrg   printf("   -t  Enable texturing\n");
60432001f49Smrg   printf("Keyboard:\n");
60532001f49Smrg   printf("   Esc  Exit\n");
60632001f49Smrg   printf("   t    Change texture image (requires -t option)\n");
60732001f49Smrg   printf("   a    Toggle animation\n");
60832001f49Smrg   printf("   s    Step rotation (when not animating)\n");
60932001f49Smrg}
61032001f49Smrg
61132001f49Smrg
61232001f49Smrgint
61332001f49Smrgmain(int argc, char *argv[])
61432001f49Smrg{
61532001f49Smrg   char *displayName = NULL;
61632001f49Smrg   int numThreads = 2;
61732001f49Smrg   Display *dpy = NULL;
61832001f49Smrg   EGLDisplay *egl_dpy = NULL;
61932001f49Smrg   int i;
62032001f49Smrg   Status threadStat;
62132001f49Smrg
62232001f49Smrg   if (argc == 1) {
62332001f49Smrg      usage();
62432001f49Smrg   }
62532001f49Smrg   else {
62632001f49Smrg      int i;
62732001f49Smrg      for (i = 1; i < argc; i++) {
62832001f49Smrg         if (strcmp(argv[i], "-display") == 0 && i + 1 < argc) {
62932001f49Smrg            displayName = argv[i + 1];
63032001f49Smrg            i++;
63132001f49Smrg         }
63232001f49Smrg         else if (strcmp(argv[i], "-p") == 0) {
63332001f49Smrg            MultiDisplays = 1;
63432001f49Smrg         }
63532001f49Smrg         else if (strcmp(argv[i], "-l") == 0) {
63632001f49Smrg            Locking = 1;
63732001f49Smrg         }
63832001f49Smrg         else if (strcmp(argv[i], "-t") == 0) {
63932001f49Smrg            Texture = 1;
64032001f49Smrg         }
64132001f49Smrg         else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) {
64232001f49Smrg            numThreads = atoi(argv[i + 1]);
64332001f49Smrg            if (numThreads < 1)
64432001f49Smrg               numThreads = 1;
64532001f49Smrg            else if (numThreads > MAX_WINTHREADS)
64632001f49Smrg               numThreads = MAX_WINTHREADS;
64732001f49Smrg            i++;
64832001f49Smrg         }
64932001f49Smrg         else {
65032001f49Smrg            usage();
65132001f49Smrg            exit(1);
65232001f49Smrg         }
65332001f49Smrg      }
65432001f49Smrg   }
65532001f49Smrg
65632001f49Smrg   if (Locking)
65732001f49Smrg      printf("xeglthreads: Using explicit locks around Xlib calls.\n");
65832001f49Smrg   else
65932001f49Smrg      printf("xeglthreads: No explict locking.\n");
66032001f49Smrg
66132001f49Smrg   if (MultiDisplays)
66232001f49Smrg      printf("xeglthreads: Per-thread display connections.\n");
66332001f49Smrg   else
66432001f49Smrg      printf("xeglthreads: Single display connection.\n");
66532001f49Smrg
66632001f49Smrg   /*
66732001f49Smrg    * VERY IMPORTANT: call XInitThreads() before any other Xlib functions.
66832001f49Smrg    */
66932001f49Smrg   if (!MultiDisplays) {
67032001f49Smrg       if (!Locking) {
67132001f49Smrg           threadStat = XInitThreads();
67232001f49Smrg           if (threadStat) {
67332001f49Smrg               printf("XInitThreads() returned %d (success)\n",
67432001f49Smrg                      (int) threadStat);
67532001f49Smrg           }
67632001f49Smrg           else {
67732001f49Smrg               printf("XInitThreads() returned 0 "
67832001f49Smrg                      "(failure- this program may fail)\n");
67932001f49Smrg           }
68032001f49Smrg       }
68132001f49Smrg
68232001f49Smrg      dpy = XOpenDisplay(displayName);
68332001f49Smrg      if (!dpy) {
68432001f49Smrg         fprintf(stderr, "Unable to open display %s\n",
68532001f49Smrg                 XDisplayName(displayName));
68632001f49Smrg         return -1;
68732001f49Smrg      }
68832001f49Smrg      egl_dpy = eglGetDisplay(dpy);
68932001f49Smrg      if (!egl_dpy) {
69032001f49Smrg         fprintf(stderr, "Unable to get EGL display\n");
69132001f49Smrg         XCloseDisplay(dpy);
69232001f49Smrg         return -1;
69332001f49Smrg      }
69432001f49Smrg      if (!eglInitialize(egl_dpy, NULL, NULL)) {
69532001f49Smrg          fprintf(stderr, "Unable to initialize EGL display\n");
69632001f49Smrg          return -1;
69732001f49Smrg      }
69832001f49Smrg   }
69932001f49Smrg
70032001f49Smrg   pthread_mutex_init(&Mutex, NULL);
70132001f49Smrg   pthread_mutex_init(&CondMutex, NULL);
70232001f49Smrg   pthread_cond_init(&CondVar, NULL);
70332001f49Smrg
70432001f49Smrg   printf("xeglthreads: creating windows\n");
70532001f49Smrg
70632001f49Smrg   NumWinThreads = numThreads;
70732001f49Smrg
70832001f49Smrg   /* Create the EGL windows and contexts */
70932001f49Smrg   for (i = 0; i < numThreads; i++) {
71032001f49Smrg      EGLContext share;
71132001f49Smrg
71232001f49Smrg      if (MultiDisplays) {
71332001f49Smrg         WinThreads[i].Dpy = XOpenDisplay(displayName);
71432001f49Smrg         assert(WinThreads[i].Dpy);
71532001f49Smrg         WinThreads[i].Display = eglGetDisplay(WinThreads[i].Dpy);
71632001f49Smrg         assert(eglInitialize(WinThreads[i].Display, NULL, NULL));
71732001f49Smrg      }
71832001f49Smrg      else {
71932001f49Smrg         WinThreads[i].Dpy = dpy;
72032001f49Smrg         WinThreads[i].Display = egl_dpy;
72132001f49Smrg      }
72232001f49Smrg      WinThreads[i].Index = i;
72332001f49Smrg      WinThreads[i].Initialized = GL_FALSE;
72432001f49Smrg
72532001f49Smrg      share = (Texture && i > 0) ? WinThreads[0].Context : 0;
72632001f49Smrg
72732001f49Smrg      create_window(&WinThreads[i], share);
72832001f49Smrg   }
72932001f49Smrg
73032001f49Smrg   printf("xeglthreads: creating threads\n");
73132001f49Smrg
73232001f49Smrg   /* Create the threads */
73332001f49Smrg   for (i = 0; i < numThreads; i++) {
73432001f49Smrg      pthread_create(&WinThreads[i].Thread, NULL, thread_function,
73532001f49Smrg                     (void*) &WinThreads[i]);
73632001f49Smrg      printf("xeglthreads: Created thread %p\n",
73732001f49Smrg              (void *) WinThreads[i].Thread);
73832001f49Smrg   }
73932001f49Smrg
74032001f49Smrg   if (MultiDisplays)
74132001f49Smrg      event_loop_multi();
74232001f49Smrg   else
74332001f49Smrg      event_loop(dpy);
74432001f49Smrg
74532001f49Smrg   clean_up();
74632001f49Smrg
74732001f49Smrg   if (MultiDisplays) {
74832001f49Smrg      for (i = 0; i < numThreads; i++) {
74932001f49Smrg          eglTerminate(WinThreads[i].Display);
75032001f49Smrg          XCloseDisplay(WinThreads[i].Dpy);
75132001f49Smrg      }
75232001f49Smrg   }
75332001f49Smrg   else {
75432001f49Smrg      eglTerminate(egl_dpy);
75532001f49Smrg      XCloseDisplay(dpy);
75632001f49Smrg   }
75732001f49Smrg
75832001f49Smrg   return 0;
75932001f49Smrg}
76032001f49Smrg
76132001f49Smrg
76232001f49Smrg#else /* PTHREADS */
76332001f49Smrg
76432001f49Smrg
76532001f49Smrg#include <stdio.h>
76632001f49Smrg
76732001f49Smrgint
76832001f49Smrgmain(int argc, char *argv[])
76932001f49Smrg{
77032001f49Smrg   printf("Sorry, this program wasn't compiled with PTHREADS defined.\n");
77132001f49Smrg   return 0;
77232001f49Smrg}
77332001f49Smrg
77432001f49Smrg
77532001f49Smrg#endif /* PTHREADS */
776