1 /* 2 Copyright (c) 2008, 2009 Apple Inc. 3 4 Permission is hereby granted, free of charge, to any person 5 obtaining a copy of this software and associated documentation files 6 (the "Software"), to deal in the Software without restriction, 7 including without limitation the rights to use, copy, modify, merge, 8 publish, distribute, sublicense, and/or sell copies of the Software, 9 and to permit persons to whom the Software is furnished to do so, 10 subject to the following conditions: 11 12 The above copyright notice and this permission notice shall be 13 included in all copies or substantial portions of the Software. 14 15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT 19 HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 DEALINGS IN THE SOFTWARE. 23 24 Except as contained in this notice, the name(s) of the above 25 copyright holders shall not be used in advertising or otherwise to 26 promote the sale, use or other dealings in this Software without 27 prior written authorization. 28 */ 29 30 #include <stdbool.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <assert.h> 34 #include <pthread.h> 35 #include <string.h> 36 #include "apple_glx.h" 37 #include "apple_glx_context.h" 38 #include "apple_glx_drawable.h" 39 #include "appledri.h" 40 41 static pthread_mutex_t drawables_lock = PTHREAD_MUTEX_INITIALIZER; 42 static struct apple_glx_drawable *drawables_list = NULL; 43 44 static void 45 lock_drawables_list(void) 46 { 47 int err; 48 49 err = pthread_mutex_lock(&drawables_lock); 50 51 if (err) { 52 fprintf(stderr, "pthread_mutex_lock failure in %s: %s\n", 53 __func__, strerror(err)); 54 abort(); 55 } 56 } 57 58 static void 59 unlock_drawables_list(void) 60 { 61 int err; 62 63 err = pthread_mutex_unlock(&drawables_lock); 64 65 if (err) { 66 fprintf(stderr, "pthread_mutex_unlock failure in %s: %s\n", 67 __func__, strerror(err)); 68 abort(); 69 } 70 } 71 72 struct apple_glx_drawable * 73 apple_glx_find_drawable(Display * dpy, GLXDrawable drawable) 74 { 75 struct apple_glx_drawable *i, *agd = NULL; 76 77 lock_drawables_list(); 78 79 for (i = drawables_list; i; i = i->next) { 80 if (i->drawable == drawable) { 81 agd = i; 82 break; 83 } 84 } 85 86 unlock_drawables_list(); 87 88 return agd; 89 } 90 91 static void 92 drawable_lock(struct apple_glx_drawable *agd) 93 { 94 int err; 95 96 err = pthread_mutex_lock(&agd->mutex); 97 98 if (err) { 99 fprintf(stderr, "pthread_mutex_lock error: %s\n", strerror(err)); 100 abort(); 101 } 102 } 103 104 static void 105 drawable_unlock(struct apple_glx_drawable *d) 106 { 107 int err; 108 109 err = pthread_mutex_unlock(&d->mutex); 110 111 if (err) { 112 fprintf(stderr, "pthread_mutex_unlock error: %s\n", strerror(err)); 113 abort(); 114 } 115 } 116 117 118 static void 119 reference_drawable(struct apple_glx_drawable *d) 120 { 121 d->lock(d); 122 d->reference_count++; 123 d->unlock(d); 124 } 125 126 static void 127 release_drawable(struct apple_glx_drawable *d) 128 { 129 d->lock(d); 130 d->reference_count--; 131 d->unlock(d); 132 } 133 134 /* The drawables list must be locked prior to calling this. */ 135 /* Return true if the drawable was destroyed. */ 136 static bool 137 destroy_drawable(struct apple_glx_drawable *d) 138 { 139 int err; 140 141 d->lock(d); 142 143 if (d->reference_count > 0) { 144 d->unlock(d); 145 return false; 146 } 147 148 d->unlock(d); 149 150 if (d->previous) { 151 d->previous->next = d->next; 152 } 153 else { 154 /* 155 * The item must be at the head of the list, if it 156 * has no previous pointer. 157 */ 158 drawables_list = d->next; 159 } 160 161 if (d->next) 162 d->next->previous = d->previous; 163 164 unlock_drawables_list(); 165 166 if (d->callbacks.destroy) { 167 /* 168 * Warning: this causes other routines to be called (potentially) 169 * from surface_notify_handler. It's probably best to not have 170 * any locks at this point locked. 171 */ 172 d->callbacks.destroy(d->display, d); 173 } 174 175 apple_glx_diagnostic("%s: freeing %p\n", __func__, (void *) d); 176 177 /* Stupid recursive locks */ 178 while (pthread_mutex_unlock(&d->mutex) == 0); 179 180 err = pthread_mutex_destroy(&d->mutex); 181 if (err) { 182 fprintf(stderr, "pthread_mutex_destroy error: %s\n", strerror(err)); 183 abort(); 184 } 185 186 free(d); 187 188 /* So that the locks are balanced and the caller correctly unlocks. */ 189 lock_drawables_list(); 190 191 return true; 192 } 193 194 /* 195 * This is typically called when a context is destroyed or the current 196 * drawable is made None. 197 */ 198 static bool 199 destroy_drawable_callback(struct apple_glx_drawable *d) 200 { 201 bool result; 202 203 d->lock(d); 204 205 apple_glx_diagnostic("%s: %p ->reference_count before -- %d\n", __func__, 206 (void *) d, d->reference_count); 207 208 d->reference_count--; 209 210 if (d->reference_count > 0) { 211 d->unlock(d); 212 return false; 213 } 214 215 d->unlock(d); 216 217 lock_drawables_list(); 218 219 result = destroy_drawable(d); 220 221 unlock_drawables_list(); 222 223 return result; 224 } 225 226 static bool 227 is_pbuffer(struct apple_glx_drawable *d) 228 { 229 return APPLE_GLX_DRAWABLE_PBUFFER == d->type; 230 } 231 232 static bool 233 is_pixmap(struct apple_glx_drawable *d) 234 { 235 return APPLE_GLX_DRAWABLE_PIXMAP == d->type; 236 } 237 238 static void 239 common_init(Display * dpy, GLXDrawable drawable, struct apple_glx_drawable *d) 240 { 241 int err; 242 pthread_mutexattr_t attr; 243 244 d->display = dpy; 245 d->reference_count = 0; 246 d->drawable = drawable; 247 d->type = -1; 248 249 err = pthread_mutexattr_init(&attr); 250 251 if (err) { 252 fprintf(stderr, "pthread_mutexattr_init error: %s\n", strerror(err)); 253 abort(); 254 } 255 256 /* 257 * There are some patterns that require a recursive mutex, 258 * when working with locks that protect the apple_glx_drawable, 259 * and reference functions like ->reference, and ->release. 260 */ 261 err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 262 263 if (err) { 264 fprintf(stderr, "error: setting pthread mutex type: %s\n", strerror(err)); 265 abort(); 266 } 267 268 err = pthread_mutex_init(&d->mutex, &attr); 269 270 if (err) { 271 fprintf(stderr, "pthread_mutex_init error: %s\n", strerror(err)); 272 abort(); 273 } 274 275 (void) pthread_mutexattr_destroy(&attr); 276 277 d->lock = drawable_lock; 278 d->unlock = drawable_unlock; 279 280 d->reference = reference_drawable; 281 d->release = release_drawable; 282 283 d->destroy = destroy_drawable_callback; 284 285 d->is_pbuffer = is_pbuffer; 286 d->is_pixmap = is_pixmap; 287 288 d->width = -1; 289 d->height = -1; 290 d->row_bytes = 0; 291 d->path[0] = '\0'; 292 d->fd = -1; 293 d->buffer = NULL; 294 d->buffer_length = 0; 295 296 d->previous = NULL; 297 d->next = NULL; 298 } 299 300 static void 301 link_tail(struct apple_glx_drawable *agd) 302 { 303 lock_drawables_list(); 304 305 /* Link the new drawable into the global list. */ 306 agd->next = drawables_list; 307 308 if (drawables_list) 309 drawables_list->previous = agd; 310 311 drawables_list = agd; 312 313 unlock_drawables_list(); 314 } 315 316 /*WARNING: this returns a locked and referenced object. */ 317 bool 318 apple_glx_drawable_create(Display * dpy, 319 int screen, 320 GLXDrawable drawable, 321 struct apple_glx_drawable **agdResult, 322 struct apple_glx_drawable_callbacks *callbacks) 323 { 324 struct apple_glx_drawable *d; 325 326 d = calloc(1, sizeof *d); 327 328 if (NULL == d) { 329 perror("malloc"); 330 return true; 331 } 332 333 common_init(dpy, drawable, d); 334 d->type = callbacks->type; 335 d->callbacks = *callbacks; 336 337 d->reference(d); 338 d->lock(d); 339 340 link_tail(d); 341 342 apple_glx_diagnostic("%s: new drawable %p\n", __func__, (void *) d); 343 344 *agdResult = d; 345 346 return false; 347 } 348 349 static int error_count = 0; 350 351 static int 352 error_handler(Display * dpy, XErrorEvent * err) 353 { 354 if (err->error_code == BadWindow) { 355 ++error_count; 356 } 357 358 return 0; 359 } 360 361 void 362 apple_glx_garbage_collect_drawables(Display * dpy) 363 { 364 struct apple_glx_drawable *d, *dnext; 365 Window root; 366 int x, y; 367 unsigned int width, height, bd, depth; 368 int (*old_handler) (Display *, XErrorEvent *); 369 370 371 if (NULL == drawables_list) 372 return; 373 374 old_handler = XSetErrorHandler(error_handler); 375 376 XSync(dpy, False); 377 378 lock_drawables_list(); 379 380 for (d = drawables_list; d;) { 381 dnext = d->next; 382 383 d->lock(d); 384 385 if (d->reference_count > 0) { 386 /* 387 * Skip this, because some context still retains a reference 388 * to the drawable. 389 */ 390 d->unlock(d); 391 d = dnext; 392 continue; 393 } 394 395 d->unlock(d); 396 397 error_count = 0; 398 399 /* 400 * Mesa uses XGetWindowAttributes, but some of these things are 401 * most definitely not Windows, and that's against the rules. 402 * XGetGeometry on the other hand is legal with a Pixmap and Window. 403 */ 404 XGetGeometry(dpy, d->drawable, &root, &x, &y, &width, &height, &bd, 405 &depth); 406 407 if (error_count > 0) { 408 /* 409 * Note: this may not actually destroy the drawable. 410 * If another context retains a reference to the drawable 411 * after the reference count test above. 412 */ 413 (void) destroy_drawable(d); 414 error_count = 0; 415 } 416 417 d = dnext; 418 } 419 420 XSetErrorHandler(old_handler); 421 422 unlock_drawables_list(); 423 } 424 425 unsigned int 426 apple_glx_get_drawable_count(void) 427 { 428 unsigned int result = 0; 429 struct apple_glx_drawable *d; 430 431 lock_drawables_list(); 432 433 for (d = drawables_list; d; d = d->next) 434 ++result; 435 436 unlock_drawables_list(); 437 438 return result; 439 } 440 441 struct apple_glx_drawable * 442 apple_glx_drawable_find_by_type(GLXDrawable drawable, int type, int flags) 443 { 444 struct apple_glx_drawable *d; 445 446 lock_drawables_list(); 447 448 for (d = drawables_list; d; d = d->next) { 449 if (d->type == type && d->drawable == drawable) { 450 if (flags & APPLE_GLX_DRAWABLE_REFERENCE) 451 d->reference(d); 452 453 if (flags & APPLE_GLX_DRAWABLE_LOCK) 454 d->lock(d); 455 456 unlock_drawables_list(); 457 458 return d; 459 } 460 } 461 462 unlock_drawables_list(); 463 464 return NULL; 465 } 466 467 struct apple_glx_drawable * 468 apple_glx_drawable_find(GLXDrawable drawable, int flags) 469 { 470 struct apple_glx_drawable *d; 471 472 lock_drawables_list(); 473 474 for (d = drawables_list; d; d = d->next) { 475 if (d->drawable == drawable) { 476 if (flags & APPLE_GLX_DRAWABLE_REFERENCE) 477 d->reference(d); 478 479 if (flags & APPLE_GLX_DRAWABLE_LOCK) 480 d->lock(d); 481 482 unlock_drawables_list(); 483 484 return d; 485 } 486 } 487 488 unlock_drawables_list(); 489 490 return NULL; 491 } 492 493 /* Return true if the type is valid for the drawable. */ 494 bool 495 apple_glx_drawable_destroy_by_type(Display * dpy, 496 GLXDrawable drawable, int type) 497 { 498 struct apple_glx_drawable *d; 499 500 lock_drawables_list(); 501 502 for (d = drawables_list; d; d = d->next) { 503 if (drawable == d->drawable && type == d->type) { 504 /* 505 * The user has requested that we destroy this resource. 506 * However, there may be references in the contexts to it, so 507 * release it, and call destroy_drawable which doesn't destroy 508 * if the reference_count is > 0. 509 */ 510 d->release(d); 511 512 apple_glx_diagnostic("%s d->reference_count %d\n", 513 __func__, d->reference_count); 514 515 destroy_drawable(d); 516 unlock_drawables_list(); 517 return true; 518 } 519 } 520 521 unlock_drawables_list(); 522 523 return false; 524 } 525 526 struct apple_glx_drawable * 527 apple_glx_drawable_find_by_uid(unsigned int uid, int flags) 528 { 529 struct apple_glx_drawable *d; 530 531 lock_drawables_list(); 532 533 for (d = drawables_list; d; d = d->next) { 534 /* Only surfaces have a uid. */ 535 if (APPLE_GLX_DRAWABLE_SURFACE == d->type) { 536 if (d->types.surface.uid == uid) { 537 if (flags & APPLE_GLX_DRAWABLE_REFERENCE) 538 d->reference(d); 539 540 if (flags & APPLE_GLX_DRAWABLE_LOCK) 541 d->lock(d); 542 543 unlock_drawables_list(); 544 545 return d; 546 } 547 } 548 } 549 550 unlock_drawables_list(); 551 552 return NULL; 553 } 554