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