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