glut_win.c revision c041511d
1 2/* Copyright (c) Mark J. Kilgard, 1994, 1997. */ 3 4/* This program is freely distributable without licensing fees 5 and is provided without guarantee or warrantee expressed or 6 implied. This program is -not- in the public domain. */ 7 8#ifdef __VMS 9#include <GL/vms_x_fix.h> 10#endif 11 12#include <stdlib.h> 13#include <stdio.h> 14#include <string.h> 15#include <assert.h> 16#if !defined(_WIN32) 17#include <X11/Xlib.h> 18#include <X11/Xatom.h> 19#endif 20 21#include "glutint.h" 22 23GLUTwindow *__glutCurrentWindow = NULL; 24GLUTwindow **__glutWindowList = NULL; 25int __glutWindowListSize = 0; 26#if !defined(_WIN32) 27GLUTstale *__glutStaleWindowList = NULL; 28#endif 29GLUTwindow *__glutMenuWindow = NULL; 30 31void (*__glutFreeOverlayFunc) (GLUToverlay *); 32XVisualInfo *(*__glutDetermineVisualFromString) (char *string, Bool * treatAsSingle, 33 Criterion * requiredCriteria, int nRequired, int requiredMask, void **fbc) = NULL; 34 35static Criterion requiredWindowCriteria[] = 36{ 37 {LEVEL, EQ, 0}, 38 {TRANSPARENT, EQ, 0} 39}; 40static int numRequiredWindowCriteria = sizeof(requiredWindowCriteria) / sizeof(Criterion); 41static int requiredWindowCriteriaMask = (1 << LEVEL) | (1 << TRANSPARENT); 42 43static void 44cleanWindowWorkList(GLUTwindow * window) 45{ 46 GLUTwindow **pEntry = &__glutWindowWorkList; 47 GLUTwindow *entry = __glutWindowWorkList; 48 49 /* Tranverse singly-linked window work list look for the 50 window. */ 51 while (entry) { 52 if (entry == window) { 53 /* Found it; delete it. */ 54 *pEntry = entry->prevWorkWin; 55 return; 56 } else { 57 pEntry = &entry->prevWorkWin; 58 entry = *pEntry; 59 } 60 } 61} 62 63#if !defined(_WIN32) 64 65static void 66cleanStaleWindowList(GLUTwindow * window) 67{ 68 GLUTstale **pEntry = &__glutStaleWindowList; 69 GLUTstale *entry = __glutStaleWindowList; 70 71 /* Tranverse singly-linked stale window list look for the 72 window ID. */ 73 while (entry) { 74 if (entry->window == window) { 75 /* Found it; delete it. */ 76 *pEntry = entry->next; 77 free(entry); 78 return; 79 } else { 80 pEntry = &entry->next; 81 entry = *pEntry; 82 } 83 } 84} 85 86#endif 87 88static GLUTwindow *__glutWindowCache = NULL; 89 90GLUTwindow * 91__glutGetWindow(Window win) 92{ 93 int i; 94 95 /* Does win belong to the last window ID looked up? */ 96 if (__glutWindowCache && (win == __glutWindowCache->win || 97 (__glutWindowCache->overlay && win == 98 __glutWindowCache->overlay->win))) { 99 return 100 __glutWindowCache; 101 } 102 /* Otherwise scan the window list looking for the window ID. */ 103 for (i = 0; i < __glutWindowListSize; i++) { 104 if (__glutWindowList[i]) { 105 if (win == __glutWindowList[i]->win) { 106 __glutWindowCache = __glutWindowList[i]; 107 return __glutWindowCache; 108 } 109 if (__glutWindowList[i]->overlay) { 110 if (win == __glutWindowList[i]->overlay->win) { 111 __glutWindowCache = __glutWindowList[i]; 112 return __glutWindowCache; 113 } 114 } 115 } 116 } 117#if !defined(_WIN32) 118 { 119 GLUTstale *entry; 120 121 /* Scan through destroyed overlay window IDs for which no 122 DestroyNotify has yet been received. */ 123 for (entry = __glutStaleWindowList; entry; entry = entry->next) { 124 if (entry->win == win) 125 return entry->window; 126 } 127 } 128#endif 129 return NULL; 130} 131 132/* CENTRY */ 133int GLUTAPIENTRY 134glutGetWindow(void) 135{ 136 if (__glutCurrentWindow) { 137 return __glutCurrentWindow->num + 1; 138 } else { 139 return 0; 140 } 141} 142/* ENDCENTRY */ 143 144void 145__glutSetWindow(GLUTwindow * window) 146{ 147 /* It is tempting to try to short-circuit the call to 148 glXMakeCurrent if we "know" we are going to make current 149 to a window we are already current to. In fact, this 150 assumption breaks when GLUT is expected to integrated with 151 other OpenGL windowing APIs that also make current to 152 OpenGL contexts. Since glXMakeCurrent short-circuits the 153 "already bound" case, GLUT avoids the temptation to do so 154 too. */ 155 __glutCurrentWindow = window; 156 157 MAKE_CURRENT_LAYER(__glutCurrentWindow); 158 159#if !defined(_WIN32) 160 /* We should be careful to force a finish between each 161 iteration through the GLUT main loop if indirect OpenGL 162 contexts are in use; indirect contexts tend to have much 163 longer latency because lots of OpenGL extension requests 164 can queue up in the X protocol stream. We accomplish this 165 by posting GLUT_FINISH_WORK to be done. */ 166 if (!__glutCurrentWindow->isDirect) 167 __glutPutOnWorkList(__glutCurrentWindow, GLUT_FINISH_WORK); 168#endif 169 170 /* If debugging is enabled, we'll want to check this window 171 for any OpenGL errors every iteration through the GLUT 172 main loop. To accomplish this, we post the 173 GLUT_DEBUG_WORK to be done on this window. */ 174 if (__glutDebug) { 175 __glutPutOnWorkList(__glutCurrentWindow, GLUT_DEBUG_WORK); 176 } 177} 178 179/* CENTRY */ 180void GLUTAPIENTRY 181glutSetWindow(int win) 182{ 183 GLUTwindow *window; 184 185 if (win < 1 || win > __glutWindowListSize) { 186 __glutWarning("glutSetWindow attempted on bogus window."); 187 return; 188 } 189 window = __glutWindowList[win - 1]; 190 if (!window) { 191 __glutWarning("glutSetWindow attempted on bogus window."); 192 return; 193 } 194 __glutSetWindow(window); 195} 196/* ENDCENTRY */ 197 198static int 199getUnusedWindowSlot(void) 200{ 201 int i; 202 203 /* Look for allocated, unused slot. */ 204 for (i = 0; i < __glutWindowListSize; i++) { 205 if (!__glutWindowList[i]) { 206 return i; 207 } 208 } 209 /* Allocate a new slot. */ 210 __glutWindowListSize++; 211 if (__glutWindowList) { 212 __glutWindowList = (GLUTwindow **) 213 realloc(__glutWindowList, 214 __glutWindowListSize * sizeof(GLUTwindow *)); 215 } else { 216 /* XXX Some realloc's do not correctly perform a malloc 217 when asked to perform a realloc on a NULL pointer, 218 though the ANSI C library spec requires this. */ 219 __glutWindowList = (GLUTwindow **) 220 malloc(sizeof(GLUTwindow *)); 221 } 222 if (!__glutWindowList) 223 __glutFatalError("out of memory."); 224 __glutWindowList[__glutWindowListSize - 1] = NULL; 225 return __glutWindowListSize - 1; 226} 227 228static XVisualInfo * 229getVisualInfoCI(unsigned int mode) 230{ 231 static int bufSizeList[] = 232 {16, 12, 8, 4, 2, 1, 0}; 233 XVisualInfo *vi; 234 int list[32]; 235 int i, n = 0; 236 237 /* Should not be looking at display mode mask if 238 __glutDisplayString is non-NULL. */ 239 assert(!__glutDisplayString); 240 241 list[n++] = GLX_BUFFER_SIZE; 242 list[n++] = 1; 243 if (GLUT_WIND_IS_DOUBLE(mode)) { 244 list[n++] = GLX_DOUBLEBUFFER; 245 } 246 if (GLUT_WIND_IS_STEREO(mode)) { 247 list[n++] = GLX_STEREO; 248 } 249 if (GLUT_WIND_HAS_DEPTH(mode)) { 250 list[n++] = GLX_DEPTH_SIZE; 251 list[n++] = 1; 252 } 253 if (GLUT_WIND_HAS_STENCIL(mode)) { 254 list[n++] = GLX_STENCIL_SIZE; 255 list[n++] = 1; 256 } 257 list[n] = (int) None; /* terminate list */ 258 259 /* glXChooseVisual specify GLX_BUFFER_SIZE prefers the 260 "smallest index buffer of at least the specified size". 261 This would be reasonable if GLUT allowed the user to 262 specify the required buffe size, but GLUT's display mode 263 is too simplistic (easy to use?). GLUT should try to find 264 the "largest". So start with a large buffer size and 265 shrink until we find a matching one that exists. */ 266 267 for (i = 0; bufSizeList[i]; i++) { 268 /* XXX Assumes list[1] is where GLX_BUFFER_SIZE parameter 269 is. */ 270 list[1] = bufSizeList[i]; 271 vi = glXChooseVisual(__glutDisplay, 272 __glutScreen, list); 273 if (vi) 274 return vi; 275 } 276 return NULL; 277} 278 279static XVisualInfo * 280getVisualInfoRGB(unsigned int mode) 281{ 282 int list[32]; 283 int n = 0; 284 285 /* Should not be looking at display mode mask if 286 __glutDisplayString is non-NULL. */ 287 assert(!__glutDisplayString); 288 289 /* XXX Would a caching mechanism to minize the calls to 290 glXChooseVisual? You'd have to reference count 291 XVisualInfo* pointers. Would also have to properly 292 interact with glutInitDisplayString. */ 293 294 list[n++] = GLX_RGBA; 295 list[n++] = GLX_RED_SIZE; 296 list[n++] = 1; 297 list[n++] = GLX_GREEN_SIZE; 298 list[n++] = 1; 299 list[n++] = GLX_BLUE_SIZE; 300 list[n++] = 1; 301 if (GLUT_WIND_HAS_ALPHA(mode)) { 302 list[n++] = GLX_ALPHA_SIZE; 303 list[n++] = 1; 304 } 305 if (GLUT_WIND_IS_DOUBLE(mode)) { 306 list[n++] = GLX_DOUBLEBUFFER; 307 } 308 if (GLUT_WIND_IS_STEREO(mode)) { 309 list[n++] = GLX_STEREO; 310 } 311 if (GLUT_WIND_HAS_DEPTH(mode)) { 312 list[n++] = GLX_DEPTH_SIZE; 313 list[n++] = 1; 314 } 315 if (GLUT_WIND_HAS_STENCIL(mode)) { 316 list[n++] = GLX_STENCIL_SIZE; 317 list[n++] = 1; 318 } 319 if (GLUT_WIND_HAS_ACCUM(mode)) { 320 list[n++] = GLX_ACCUM_RED_SIZE; 321 list[n++] = 1; 322 list[n++] = GLX_ACCUM_GREEN_SIZE; 323 list[n++] = 1; 324 list[n++] = GLX_ACCUM_BLUE_SIZE; 325 list[n++] = 1; 326 if (GLUT_WIND_HAS_ALPHA(mode)) { 327 list[n++] = GLX_ACCUM_ALPHA_SIZE; 328 list[n++] = 1; 329 } 330 } 331#if defined(GLX_VERSION_1_1) && (defined(GLX_SGIS_multisample) || defined(GLX_ARB_multisample)) 332 if (GLUT_WIND_IS_MULTISAMPLE(mode)) { 333 if (!__glutIsSupportedByGLX("GLX_SGIS_multisample") && 334 !__glutIsSupportedByGLX("GLX_ARB_multisample")) 335 return NULL; 336#if defined(GLX_ARB_multisample) 337 list[n++] = GLX_SAMPLES_ARB; 338#elif defined(GLX_SGIS_multisample) 339 list[n++] = GLX_SAMPLES_SGIS; 340#endif 341 /* XXX Is 4 a reasonable minimum acceptable number of 342 samples? */ 343 list[n++] = 4; 344 } 345#endif 346 list[n] = (int) None; /* terminate list */ 347 348 return glXChooseVisual(__glutDisplay, 349 __glutScreen, list); 350} 351 352#ifndef VisualIDMask 353#define VisualIDMask 0 354#endif 355 356static XVisualInfo * 357getVisualInfoID(int id) 358{ 359 XVisualInfo temp; 360 int count; 361#if !defined(_WIN32) 362 temp.visualid = id; 363#endif 364 return XGetVisualInfo(__glutDisplay, VisualIDMask, &temp, &count); 365} 366 367 368XVisualInfo * 369__glutGetVisualInfo(unsigned int mode) 370{ 371 char *visStr; 372 /* XXX GLUT_LUMINANCE not implemented for GLUT 3.0. */ 373 if (GLUT_WIND_IS_LUMINANCE(mode)) 374 return NULL; 375 376 visStr = getenv("GLUT_FORCE_VISUAL"); 377 if (visStr) { 378 int id = atoi(visStr); 379 return getVisualInfoID(id); 380 } 381 382 if (GLUT_WIND_IS_RGB(mode)) 383 return getVisualInfoRGB(mode); 384 else 385 return getVisualInfoCI(mode); 386} 387 388XVisualInfo * 389__glutDetermineVisual( 390 unsigned int displayMode, 391 Bool * treatAsSingle, 392 XVisualInfo * (getVisualInfo) (unsigned int)) 393{ 394 XVisualInfo *vis; 395 396 /* Should not be looking at display mode mask if 397 __glutDisplayString is non-NULL. */ 398 assert(!__glutDisplayString); 399 400 *treatAsSingle = GLUT_WIND_IS_SINGLE(displayMode); 401 vis = getVisualInfo(displayMode); 402 if (!vis) { 403 /* Fallback cases when can't get exactly what was asked 404 for... */ 405 if (GLUT_WIND_IS_SINGLE(displayMode)) { 406 /* If we can't find a single buffered visual, try looking 407 for a double buffered visual. We can treat a double 408 buffered visual as a single buffer visual by changing 409 the draw buffer to GL_FRONT and treating any swap 410 buffers as no-ops. */ 411 displayMode |= GLUT_DOUBLE; 412 vis = getVisualInfo(displayMode); 413 *treatAsSingle = True; 414 } 415 if (!vis && GLUT_WIND_IS_MULTISAMPLE(displayMode)) { 416 /* If we can't seem to get multisampling (ie, not Reality 417 Engine class graphics!), go without multisampling. It 418 is up to the application to query how many multisamples 419 were allocated (0 equals no multisampling) if the 420 application is going to use multisampling for more than 421 just antialiasing. */ 422 displayMode &= ~GLUT_MULTISAMPLE; 423 vis = getVisualInfo(displayMode); 424 } 425 } 426 return vis; 427} 428 429static void GLUTCALLBACK 430__glutDefaultDisplay(void) 431{ 432 /* XXX Remove the warning after GLUT 3.0. */ 433 __glutWarning("The following is a new check for GLUT 3.0; update your code."); 434 __glutFatalError( 435 "redisplay needed for window %d, but no display callback.", 436 __glutCurrentWindow->num + 1); 437} 438 439void GLUTCALLBACK 440__glutDefaultReshape(int width, int height) 441{ 442 GLUToverlay *overlay; 443 444 /* Adjust the viewport of the window (and overlay if one 445 exists). */ 446 MAKE_CURRENT_WINDOW(__glutCurrentWindow); 447 glViewport(0, 0, (GLsizei) width, (GLsizei) height); 448 overlay = __glutCurrentWindow->overlay; 449 if (overlay) { 450 MAKE_CURRENT_OVERLAY(overlay); 451 glViewport(0, 0, (GLsizei) width, (GLsizei) height); 452 } 453 /* Make sure we are current to the current layer (application 454 should be able to count on the current layer not changing 455 unless the application explicitly calls glutUseLayer). */ 456 MAKE_CURRENT_LAYER(__glutCurrentWindow); 457} 458 459XVisualInfo * 460__glutDetermineWindowVisual(Bool * treatAsSingle, Bool * visAlloced, void **fbc) 461{ 462 if (__glutDisplayString) { 463 464 /* __glutDisplayString should be NULL except if 465 glutInitDisplayString has been called to register a 466 different display string. Calling glutInitDisplayString 467 means using a string instead of an integer mask determine 468 the visual to use. Using the function pointer variable 469 __glutDetermineVisualFromString below avoids linking in 470 the code for implementing glutInitDisplayString (ie, 471 glut_dstr.o) unless glutInitDisplayString gets called by 472 the application. */ 473 474 assert(__glutDetermineVisualFromString); 475 *visAlloced = False; 476 *fbc = NULL; 477 return __glutDetermineVisualFromString(__glutDisplayString, treatAsSingle, 478 requiredWindowCriteria, numRequiredWindowCriteria, requiredWindowCriteriaMask, fbc); 479 } else { 480 *visAlloced = True; 481 *fbc = NULL; 482 return __glutDetermineVisual(__glutDisplayMode, 483 treatAsSingle, __glutGetVisualInfo); 484 } 485} 486 487/* ARGSUSED5 */ /* Only Win32 uses gameMode parameter. */ 488GLUTwindow * 489__glutCreateWindow(GLUTwindow * parent, 490 int x, int y, int width, int height, int gameMode) 491{ 492 GLUTwindow *window; 493 XSetWindowAttributes wa; 494 unsigned long attribMask; 495 int winnum; 496 int i; 497 void *fbc; 498 499#if defined(_WIN32) 500 WNDCLASS wc; 501 int style; 502 503 if (!GetClassInfo(GetModuleHandle(NULL), "GLUT", &wc)) { 504 __glutOpenWin32Connection(NULL); 505 } 506#else 507 if (!__glutDisplay) { 508 __glutOpenXConnection(NULL); 509 } 510#endif 511 if (__glutGameModeWindow) { 512 __glutFatalError("cannot create windows in game mode."); 513 } 514 winnum = getUnusedWindowSlot(); 515 window = (GLUTwindow *) malloc(sizeof(GLUTwindow)); 516 if (!window) { 517 __glutFatalError("out of memory."); 518 } 519 window->num = winnum; 520 521#if !defined(_WIN32) 522 window->vis = __glutDetermineWindowVisual(&window->treatAsSingle, 523 &window->visAlloced, &fbc); 524 if (!window->vis) { 525 __glutFatalError( 526 "visual with necessary capabilities not found."); 527 } 528 __glutSetupColormap(window->vis, &window->colormap, &window->cmap); 529#endif 530 window->eventMask = StructureNotifyMask | ExposureMask; 531 532 attribMask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask; 533 wa.background_pixmap = None; 534 wa.border_pixel = 0; 535 wa.colormap = window->cmap; 536 wa.event_mask = window->eventMask; 537 if (parent) { 538 if (parent->eventMask & GLUT_HACK_STOP_PROPAGATE_MASK) 539 wa.event_mask |= GLUT_HACK_STOP_PROPAGATE_MASK; 540 attribMask |= CWDontPropagate; 541 wa.do_not_propagate_mask = parent->eventMask & GLUT_DONT_PROPAGATE_FILTER_MASK; 542 } else { 543 wa.do_not_propagate_mask = 0; 544 } 545 546 /* Stash width and height before Win32's __glutAdjustCoords 547 possibly overwrites the values. */ 548 window->width = width; 549 window->height = height; 550 window->forceReshape = True; 551 window->ignoreKeyRepeat = False; 552 553#if defined(_WIN32) 554 __glutAdjustCoords(parent ? parent->win : NULL, 555 &x, &y, &width, &height); 556 if (parent) { 557 style = WS_CHILD; 558 } else { 559 if (gameMode) { 560 /* Game mode window should be a WS_POPUP window to 561 ensure that the taskbar is hidden by it. A standard 562 WS_OVERLAPPEDWINDOW does not hide the task bar. */ 563 style = WS_POPUP | WS_MAXIMIZE; 564 } else { 565 /* A standard toplevel window with borders and such. */ 566 style = WS_OVERLAPPEDWINDOW; 567 } 568 } 569 window->win = CreateWindow("GLUT", "GLUT", 570 WS_CLIPSIBLINGS | WS_CLIPCHILDREN | style, 571 x, y, width, height, parent ? parent->win : __glutRoot, 572 NULL, GetModuleHandle(NULL), 0); 573 window->hdc = GetDC(window->win); 574 /* Must set the XHDC for fake glXChooseVisual & fake 575 glXCreateContext & fake XAllocColorCells. */ 576 XHDC = window->hdc; 577 window->vis = __glutDetermineWindowVisual(&window->treatAsSingle, 578 &window->visAlloced, &fbc); 579 if (!window->vis) { 580 __glutFatalError( 581 "pixel format with necessary capabilities not found."); 582 } 583 if (!SetPixelFormat(window->hdc, 584 ChoosePixelFormat(window->hdc, window->vis), 585 window->vis)) { 586 __glutFatalError("SetPixelFormat failed during window create."); 587 } 588 __glutSetupColormap(window->vis, &window->colormap, &window->cmap); 589 /* Make sure subwindows get a windowStatus callback. */ 590 if (parent) { 591 PostMessage(parent->win, WM_ACTIVATE, 0, 0); 592 } 593 window->renderDc = window->hdc; 594#else 595 window->win = XCreateWindow(__glutDisplay, 596 parent == NULL ? __glutRoot : parent->win, 597 x, y, width, height, 0, 598 window->vis->depth, InputOutput, window->vis->visual, 599 attribMask, &wa); 600#endif 601 window->renderWin = window->win; 602#if defined(GLX_VERSION_1_1) && defined(GLX_SGIX_fbconfig) 603 if (fbc) { 604 window->ctx = __glut_glXCreateContextWithConfigSGIX(__glutDisplay, fbc, 605 GLX_RGBA_TYPE_SGIX, None, __glutTryDirect); 606 } else 607#endif 608 { 609 window->ctx = glXCreateContext(__glutDisplay, window->vis, 610 None, __glutTryDirect); 611 } 612 if (!window->ctx) { 613 __glutFatalError( 614 "failed to create OpenGL rendering context."); 615 } 616 window->renderCtx = window->ctx; 617#if !defined(_WIN32) 618 window->isDirect = glXIsDirect(__glutDisplay, window->ctx); 619 if (__glutForceDirect) { 620 if (!window->isDirect) 621 __glutFatalError("direct rendering not possible."); 622 } 623#endif 624 625 window->parent = parent; 626 if (parent) { 627 window->siblings = parent->children; 628 parent->children = window; 629 } else { 630 window->siblings = NULL; 631 } 632 window->overlay = NULL; 633 window->children = NULL; 634 window->display = __glutDefaultDisplay; 635 window->reshape = __glutDefaultReshape; 636 window->mouse = NULL; 637 window->motion = NULL; 638 window->passive = NULL; 639 window->entry = NULL; 640 window->keyboard = NULL; 641 window->keyboardUp = NULL; 642 window->windowStatus = NULL; 643 window->visibility = NULL; 644 window->special = NULL; 645 window->specialUp = NULL; 646 window->buttonBox = NULL; 647 window->dials = NULL; 648 window->spaceMotion = NULL; 649 window->spaceRotate = NULL; 650 window->spaceButton = NULL; 651 window->tabletMotion = NULL; 652 window->tabletButton = NULL; 653#ifdef _WIN32 654 window->joystick = NULL; 655 window->joyPollInterval = 0; 656#endif 657 window->tabletPos[0] = -1; 658 window->tabletPos[1] = -1; 659 window->shownState = 0; 660 window->visState = -1; /* not VisibilityUnobscured, 661 VisibilityPartiallyObscured, or 662 VisibilityFullyObscured */ 663 window->entryState = -1; /* not EnterNotify or LeaveNotify */ 664 665 window->desiredConfMask = 0; 666 window->buttonUses = 0; 667 window->cursor = GLUT_CURSOR_INHERIT; 668 669 /* Setup window to be mapped when glutMainLoop starts. */ 670 window->workMask = GLUT_MAP_WORK; 671#ifdef _WIN32 672 if (gameMode) { 673 /* When mapping a game mode window, just show 674 the window. We have already created the game 675 mode window with a maximize flag at creation 676 time. Doing a ShowWindow(window->win, SW_SHOWNORMAL) 677 would be wrong for a game mode window since it 678 would unmaximize the window. */ 679 window->desiredMapState = GameModeState; 680 } else { 681 window->desiredMapState = NormalState; 682 } 683#else 684 window->desiredMapState = NormalState; 685#endif 686 window->prevWorkWin = __glutWindowWorkList; 687 __glutWindowWorkList = window; 688 689 /* Initially, no menus attached. */ 690 for (i = 0; i < GLUT_MAX_MENUS; i++) { 691 window->menu[i] = 0; 692 } 693 694 /* Add this new window to the window list. */ 695 __glutWindowList[winnum] = window; 696 697 /* Make the new window the current window. */ 698 __glutSetWindow(window); 699 700 __glutDetermineMesaSwapHackSupport(); 701 702 if (window->treatAsSingle) { 703 /* We do this because either the window really is single 704 buffered (in which case this is redundant, but harmless, 705 because this is the initial single-buffered context 706 state); or we are treating a double buffered window as a 707 single-buffered window because the system does not appear 708 to export any suitable single- buffered visuals (in which 709 the following are necessary). */ 710 glDrawBuffer(GL_FRONT); 711 glReadBuffer(GL_FRONT); 712 } 713 return window; 714} 715 716/* CENTRY */ 717int GLUTAPIENTRY 718glutCreateWindow(const char *title) 719{ 720 static int firstWindow = 1; 721 GLUTwindow *window; 722#if !defined(_WIN32) 723 XWMHints *wmHints; 724#endif 725 Window win; 726 XTextProperty textprop; 727 728 if (__glutGameModeWindow) { 729 __glutFatalError("cannot create windows in game mode."); 730 } 731 window = __glutCreateWindow(NULL, 732 __glutSizeHints.x, __glutSizeHints.y, 733 __glutInitWidth, __glutInitHeight, 734 /* not game mode */ 0); 735 win = window->win; 736 /* Setup ICCCM properties. */ 737 textprop.value = (unsigned char *) title; 738 textprop.encoding = XA_STRING; 739 textprop.format = 8; 740 textprop.nitems = strlen(title); 741#if defined(_WIN32) 742 SetWindowText(win, title); 743 if (__glutIconic) { 744 window->desiredMapState = IconicState; 745 } 746#else 747 wmHints = XAllocWMHints(); 748 wmHints->initial_state = 749 __glutIconic ? IconicState : NormalState; 750 wmHints->flags = StateHint; 751 XSetWMProperties(__glutDisplay, win, &textprop, &textprop, 752 /* Only put WM_COMMAND property on first window. */ 753 firstWindow ? __glutArgv : NULL, 754 firstWindow ? __glutArgc : 0, 755 &__glutSizeHints, wmHints, NULL); 756 XFree(wmHints); 757 XSetWMProtocols(__glutDisplay, win, &__glutWMDeleteWindow, 1); 758#endif 759 firstWindow = 0; 760 return window->num + 1; 761} 762 763#ifdef _WIN32 764int GLUTAPIENTRY 765__glutCreateWindowWithExit(const char *title, void (__cdecl *exitfunc)(int)) 766{ 767 __glutExitFunc = exitfunc; 768 return glutCreateWindow(title); 769} 770#endif 771 772int GLUTAPIENTRY 773glutCreateSubWindow(int win, int x, int y, int width, int height) 774{ 775 GLUTwindow *window; 776 777 window = __glutCreateWindow(__glutWindowList[win - 1], 778 x, y, width, height, /* not game mode */ 0); 779#if !defined(_WIN32) 780 { 781 GLUTwindow *toplevel; 782 783 toplevel = __glutToplevelOf(window); 784 if (toplevel->cmap != window->cmap) { 785 __glutPutOnWorkList(toplevel, GLUT_COLORMAP_WORK); 786 } 787 } 788#endif 789 return window->num + 1; 790} 791/* ENDCENTRY */ 792 793void 794__glutDestroyWindow(GLUTwindow * window, 795 GLUTwindow * initialWindow) 796{ 797 GLUTwindow **prev, *cur, *parent, *siblings; 798 799 /* Recursively destroy any children. */ 800 cur = window->children; 801 while (cur) { 802 siblings = cur->siblings; 803 __glutDestroyWindow(cur, initialWindow); 804 cur = siblings; 805 } 806 /* Remove from parent's children list (only necessary for 807 non-initial windows and subwindows!). */ 808 parent = window->parent; 809 if (parent && parent == initialWindow->parent) { 810 prev = &parent->children; 811 cur = parent->children; 812 while (cur) { 813 if (cur == window) { 814 *prev = cur->siblings; 815 break; 816 } 817 prev = &(cur->siblings); 818 cur = cur->siblings; 819 } 820 } 821 /* Unbind if bound to this window. */ 822 if (window == __glutCurrentWindow) { 823 UNMAKE_CURRENT(); 824 __glutCurrentWindow = NULL; 825 } 826 /* Begin tearing down window itself. */ 827 if (window->overlay) { 828 __glutFreeOverlayFunc(window->overlay); 829 } 830 XDestroyWindow(__glutDisplay, window->win); 831 glXDestroyContext(__glutDisplay, window->ctx); 832 if (window->colormap) { 833 /* Only color index windows have colormap data structure. */ 834 __glutFreeColormap(window->colormap); 835 } 836 /* NULLing the __glutWindowList helps detect is a window 837 instance has been destroyed, given a window number. */ 838 __glutWindowList[window->num] = NULL; 839 840 /* Cleanup data structures that might contain window. */ 841 cleanWindowWorkList(window); 842#if !defined(_WIN32) 843 cleanStaleWindowList(window); 844#endif 845 /* Remove window from the "get window cache" if it is there. */ 846 if (__glutWindowCache == window) 847 __glutWindowCache = NULL; 848 849 if (window->visAlloced) { 850 /* Only free XVisualInfo* gotten from glXChooseVisual. */ 851 XFree(window->vis); 852 } 853 854 if (window == __glutGameModeWindow) { 855 /* Destroying the game mode window should implicitly 856 have GLUT leave game mode. */ 857 __glutCloseDownGameMode(); 858 } 859 860 free(window); 861} 862 863/* CENTRY */ 864void GLUTAPIENTRY 865glutDestroyWindow(int win) 866{ 867 GLUTwindow *window = __glutWindowList[win - 1]; 868 869 if (__glutMappedMenu && __glutMenuWindow == window) { 870 __glutFatalUsage("destroying menu window not allowed while menus in use"); 871 } 872#if !defined(_WIN32) 873 /* If not a toplevel window... */ 874 if (window->parent) { 875 /* Destroying subwindows may change colormap requirements; 876 recalculate toplevel window's WM_COLORMAP_WINDOWS 877 property. */ 878 __glutPutOnWorkList(__glutToplevelOf(window->parent), 879 GLUT_COLORMAP_WORK); 880 } 881#endif 882 __glutDestroyWindow(window, window); 883 XFlush(__glutDisplay); 884} 885/* ENDCENTRY */ 886 887void 888__glutChangeWindowEventMask(long eventMask, Bool add) 889{ 890 if (add) { 891 /* Add eventMask to window's event mask. */ 892 if ((__glutCurrentWindow->eventMask & eventMask) != 893 eventMask) { 894 __glutCurrentWindow->eventMask |= eventMask; 895 __glutPutOnWorkList(__glutCurrentWindow, 896 GLUT_EVENT_MASK_WORK); 897 } 898 } else { 899 /* Remove eventMask from window's event mask. */ 900 if (__glutCurrentWindow->eventMask & eventMask) { 901 __glutCurrentWindow->eventMask &= ~eventMask; 902 __glutPutOnWorkList(__glutCurrentWindow, 903 GLUT_EVENT_MASK_WORK); 904 } 905 } 906} 907 908void GLUTAPIENTRY 909glutDisplayFunc(GLUTdisplayCB displayFunc) 910{ 911 /* XXX Remove the warning after GLUT 3.0. */ 912 if (!displayFunc) 913 __glutFatalError("NULL display callback not allowed in GLUT 3.0; update your code."); 914 __glutCurrentWindow->display = displayFunc; 915} 916 917void GLUTAPIENTRY 918glutMouseFunc(GLUTmouseCB mouseFunc) 919{ 920 if (__glutCurrentWindow->mouse) { 921 if (!mouseFunc) { 922 /* Previous mouseFunc being disabled. */ 923 __glutCurrentWindow->buttonUses--; 924 __glutChangeWindowEventMask( 925 ButtonPressMask | ButtonReleaseMask, 926 __glutCurrentWindow->buttonUses > 0); 927 } 928 } else { 929 if (mouseFunc) { 930 /* Previously no mouseFunc, new one being installed. */ 931 __glutCurrentWindow->buttonUses++; 932 __glutChangeWindowEventMask( 933 ButtonPressMask | ButtonReleaseMask, True); 934 } 935 } 936 __glutCurrentWindow->mouse = mouseFunc; 937} 938 939void GLUTAPIENTRY 940glutMotionFunc(GLUTmotionCB motionFunc) 941{ 942 /* Hack. Some window managers (4Dwm by default) will mask 943 motion events if the client is not selecting for button 944 press and release events. So we select for press and 945 release events too (being careful to use reference 946 counting). */ 947 if (__glutCurrentWindow->motion) { 948 if (!motionFunc) { 949 /* previous mouseFunc being disabled */ 950 __glutCurrentWindow->buttonUses--; 951 __glutChangeWindowEventMask( 952 ButtonPressMask | ButtonReleaseMask, 953 __glutCurrentWindow->buttonUses > 0); 954 } 955 } else { 956 if (motionFunc) { 957 /* Previously no mouseFunc, new one being installed. */ 958 __glutCurrentWindow->buttonUses++; 959 __glutChangeWindowEventMask( 960 ButtonPressMask | ButtonReleaseMask, True); 961 } 962 } 963 /* Real work of selecting for passive mouse motion. */ 964 __glutChangeWindowEventMask( 965 Button1MotionMask | Button2MotionMask | Button3MotionMask, 966 motionFunc != NULL); 967 __glutCurrentWindow->motion = motionFunc; 968} 969 970void GLUTAPIENTRY 971glutPassiveMotionFunc(GLUTpassiveCB passiveMotionFunc) 972{ 973 __glutChangeWindowEventMask(PointerMotionMask, 974 passiveMotionFunc != NULL); 975 976 /* Passive motion also requires watching enters and leaves so 977 that a fake passive motion event can be generated on an 978 enter. */ 979 __glutChangeWindowEventMask(EnterWindowMask | LeaveWindowMask, 980 __glutCurrentWindow->entry != NULL || passiveMotionFunc != NULL); 981 982 __glutCurrentWindow->passive = passiveMotionFunc; 983} 984 985void GLUTAPIENTRY 986glutEntryFunc(GLUTentryCB entryFunc) 987{ 988 __glutChangeWindowEventMask(EnterWindowMask | LeaveWindowMask, 989 entryFunc != NULL || __glutCurrentWindow->passive); 990 __glutCurrentWindow->entry = entryFunc; 991 if (!entryFunc) { 992 __glutCurrentWindow->entryState = -1; 993 } 994} 995 996void GLUTAPIENTRY 997glutWindowStatusFunc(GLUTwindowStatusCB windowStatusFunc) 998{ 999 __glutChangeWindowEventMask(VisibilityChangeMask, 1000 windowStatusFunc != NULL); 1001 __glutCurrentWindow->windowStatus = windowStatusFunc; 1002 if (!windowStatusFunc) { 1003 /* Make state invalid. */ 1004 __glutCurrentWindow->visState = -1; 1005 } 1006} 1007 1008static void GLUTCALLBACK 1009visibilityHelper(int status) 1010{ 1011 if (status == GLUT_HIDDEN || status == GLUT_FULLY_COVERED) 1012 __glutCurrentWindow->visibility(GLUT_NOT_VISIBLE); 1013 else 1014 __glutCurrentWindow->visibility(GLUT_VISIBLE); 1015} 1016 1017void GLUTAPIENTRY 1018glutVisibilityFunc(GLUTvisibilityCB visibilityFunc) 1019{ 1020 __glutCurrentWindow->visibility = visibilityFunc; 1021 if (visibilityFunc) 1022 glutWindowStatusFunc(visibilityHelper); 1023 else 1024 glutWindowStatusFunc(NULL); 1025} 1026 1027void GLUTAPIENTRY 1028glutReshapeFunc(GLUTreshapeCB reshapeFunc) 1029{ 1030 if (reshapeFunc) { 1031 __glutCurrentWindow->reshape = reshapeFunc; 1032 } else { 1033 __glutCurrentWindow->reshape = __glutDefaultReshape; 1034 } 1035} 1036