xprFrame.c revision 35c4bbdf
1/* 2 * Xplugin rootless implementation frame functions 3 * 4 * Copyright (c) 2002-2012 Apple Computer, Inc. All rights reserved. 5 * Copyright (c) 2003 Torrey T. Lyons. All Rights Reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the "Software"), 9 * to deal in the Software without restriction, including without limitation 10 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 * and/or sell copies of the Software, and to permit persons to whom the 12 * Software is furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included in 15 * all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 * DEALINGS IN THE SOFTWARE. 24 * 25 * Except as contained in this notice, the name(s) of the above copyright 26 * holders shall not be used in advertising or otherwise to promote the sale, 27 * use or other dealings in this Software without prior written authorization. 28 */ 29 30#ifdef HAVE_DIX_CONFIG_H 31#include <dix-config.h> 32#endif 33 34#include "xpr.h" 35#include "rootlessCommon.h" 36#include <Xplugin.h> 37#include "x-hash.h" 38#include "applewmExt.h" 39 40#include "propertyst.h" 41#include "dix.h" 42#include <X11/Xatom.h> 43#include "windowstr.h" 44#include "quartz.h" 45 46#ifdef HAVE_LIBDISPATCH 47#include <dispatch/dispatch.h> 48#else 49#include <pthread.h> 50#endif 51 52#ifdef DEBUG_XP_LOCK_WINDOW 53#include <execinfo.h> 54#endif 55 56#define DEFINE_ATOM_HELPER(func, atom_name) \ 57 static Atom func(void) { \ 58 static int generation; \ 59 static Atom atom; \ 60 if (generation != serverGeneration) { \ 61 generation = serverGeneration; \ 62 atom = MakeAtom(atom_name, strlen(atom_name), TRUE); \ 63 } \ 64 return atom; \ 65 } 66 67DEFINE_ATOM_HELPER(xa_native_window_id, "_NATIVE_WINDOW_ID") 68 69/* Maps xp_window_id -> RootlessWindowRec */ 70static x_hash_table * window_hash; 71 72/* Need to guard window_hash since xprIsX11Window can be called from any thread. */ 73#ifdef HAVE_LIBDISPATCH 74static dispatch_queue_t window_hash_serial_q; 75#else 76static pthread_rwlock_t window_hash_rwlock; 77#endif 78 79/* Prototypes for static functions */ 80static Bool 81xprCreateFrame(RootlessWindowPtr pFrame, ScreenPtr pScreen, int newX, 82 int newY, 83 RegionPtr pShape); 84static void 85xprDestroyFrame(RootlessFrameID wid); 86static void 87xprMoveFrame(RootlessFrameID wid, ScreenPtr pScreen, int newX, int newY); 88static void 89xprResizeFrame(RootlessFrameID wid, ScreenPtr pScreen, int newX, int newY, 90 unsigned int newW, unsigned int newH, 91 unsigned int gravity); 92static void 93xprRestackFrame(RootlessFrameID wid, RootlessFrameID nextWid); 94static void 95xprReshapeFrame(RootlessFrameID wid, RegionPtr pShape); 96static void 97xprUnmapFrame(RootlessFrameID wid); 98static void 99xprStartDrawing(RootlessFrameID wid, char **pixelData, int *bytesPerRow); 100static void 101xprStopDrawing(RootlessFrameID wid, Bool flush); 102static void 103xprUpdateRegion(RootlessFrameID wid, RegionPtr pDamage); 104static void 105xprDamageRects(RootlessFrameID wid, int nrects, const BoxRec *rects, 106 int shift_x, 107 int shift_y); 108static void 109xprSwitchWindow(RootlessWindowPtr pFrame, WindowPtr oldWin); 110static Bool 111xprDoReorderWindow(RootlessWindowPtr pFrame); 112static void 113xprHideWindow(RootlessFrameID wid); 114static void 115xprUpdateColormap(RootlessFrameID wid, ScreenPtr pScreen); 116static void 117xprCopyWindow(RootlessFrameID wid, int dstNrects, const BoxRec *dstRects, 118 int dx, 119 int dy); 120 121static inline xp_error 122xprConfigureWindow(xp_window_id id, unsigned int mask, 123 const xp_window_changes *values) 124{ 125 return xp_configure_window(id, mask, values); 126} 127 128static void 129xprSetNativeProperty(RootlessWindowPtr pFrame) 130{ 131 xp_error err; 132 unsigned int native_id; 133 long data; 134 135 err = xp_get_native_window(x_cvt_vptr_to_uint(pFrame->wid), &native_id); 136 if (err == Success) { 137 /* FIXME: move this to AppleWM extension */ 138 139 data = native_id; 140 dixChangeWindowProperty(serverClient, pFrame->win, 141 xa_native_window_id(), 142 XA_INTEGER, 32, PropModeReplace, 1, &data, 143 TRUE); 144 } 145} 146 147static xp_error 148xprColormapCallback(void *data, int first_color, int n_colors, 149 uint32_t *colors) 150{ 151 return (RootlessResolveColormap(data, first_color, n_colors, 152 colors) ? XP_Success : XP_BadMatch); 153} 154 155/* 156 * Create and display a new frame. 157 */ 158static Bool 159xprCreateFrame(RootlessWindowPtr pFrame, ScreenPtr pScreen, 160 int newX, int newY, RegionPtr pShape) 161{ 162 WindowPtr pWin = pFrame->win; 163 xp_window_changes wc; 164 unsigned int mask = 0; 165 xp_error err; 166 167 wc.x = newX; 168 wc.y = newY; 169 wc.width = pFrame->width; 170 wc.height = pFrame->height; 171 wc.bit_gravity = XP_GRAVITY_NONE; 172 mask |= XP_BOUNDS; 173 174 if (pWin->drawable.depth == 8) { 175 wc.depth = XP_DEPTH_INDEX8; 176 wc.colormap = xprColormapCallback; 177 wc.colormap_data = pScreen; 178 mask |= XP_COLORMAP; 179 } 180 else if (pWin->drawable.depth == 15) 181 wc.depth = XP_DEPTH_RGB555; 182 else if (pWin->drawable.depth == 24) 183 wc.depth = XP_DEPTH_ARGB8888; 184 else 185 wc.depth = XP_DEPTH_NIL; 186 mask |= XP_DEPTH; 187 188 if (pShape != NULL) { 189 wc.shape_nrects = RegionNumRects(pShape); 190 wc.shape_rects = RegionRects(pShape); 191 wc.shape_tx = wc.shape_ty = 0; 192 mask |= XP_SHAPE; 193 } 194 195 pFrame->level = 196 !IsRoot(pWin) ? AppleWMWindowLevelNormal : AppleWMNumWindowLevels; 197 198 if (XQuartzIsRootless) 199 wc.window_level = normal_window_levels[pFrame->level]; 200 else if (XQuartzShieldingWindowLevel) 201 wc.window_level = XQuartzShieldingWindowLevel + 1; 202 else 203 wc.window_level = rooted_window_levels[pFrame->level]; 204 mask |= XP_WINDOW_LEVEL; 205 206 err = xp_create_window(mask, &wc, (xp_window_id *)&pFrame->wid); 207 208 if (err != Success) { 209 return FALSE; 210 } 211 212#ifdef HAVE_LIBDISPATCH 213 dispatch_async(window_hash_serial_q, ^ { 214 x_hash_table_insert(window_hash, pFrame->wid, pFrame); 215 }); 216#else 217 pthread_rwlock_wrlock(&window_hash_rwlock); 218 x_hash_table_insert(window_hash, pFrame->wid, pFrame); 219 pthread_rwlock_unlock(&window_hash_rwlock); 220#endif 221 222 xprSetNativeProperty(pFrame); 223 224 return TRUE; 225} 226 227/* 228 * Destroy a frame. 229 */ 230static void 231xprDestroyFrame(RootlessFrameID wid) 232{ 233 xp_error err; 234 235#ifdef HAVE_LIBDISPATCH 236 dispatch_async(window_hash_serial_q, ^ { 237 x_hash_table_remove(window_hash, wid); 238 }); 239#else 240 pthread_rwlock_wrlock(&window_hash_rwlock); 241 x_hash_table_remove(window_hash, wid); 242 pthread_rwlock_unlock(&window_hash_rwlock); 243#endif 244 245 err = xp_destroy_window(x_cvt_vptr_to_uint(wid)); 246 if (err != Success) 247 FatalError("Could not destroy window %d (%d).", 248 (int)x_cvt_vptr_to_uint( 249 wid), (int)err); 250} 251 252/* 253 * Move a frame on screen. 254 */ 255static void 256xprMoveFrame(RootlessFrameID wid, ScreenPtr pScreen, int newX, int newY) 257{ 258 xp_window_changes wc; 259 260 wc.x = newX; 261 wc.y = newY; 262 // ErrorF("xprMoveFrame(%d, %p, %d, %d)\n", wid, pScreen, newX, newY); 263 xprConfigureWindow(x_cvt_vptr_to_uint(wid), XP_ORIGIN, &wc); 264} 265 266/* 267 * Resize and move a frame. 268 */ 269static void 270xprResizeFrame(RootlessFrameID wid, ScreenPtr pScreen, 271 int newX, int newY, unsigned int newW, unsigned int newH, 272 unsigned int gravity) 273{ 274 xp_window_changes wc; 275 276 wc.x = newX; 277 wc.y = newY; 278 wc.width = newW; 279 wc.height = newH; 280 wc.bit_gravity = gravity; 281 282 /* It's unlikely that being async will save us anything here. 283 But it can't hurt. */ 284 285 xprConfigureWindow(x_cvt_vptr_to_uint(wid), XP_BOUNDS, &wc); 286} 287 288/* 289 * Change frame stacking. 290 */ 291static void 292xprRestackFrame(RootlessFrameID wid, RootlessFrameID nextWid) 293{ 294 xp_window_changes wc; 295 unsigned int mask = XP_STACKING; 296#ifdef HAVE_LIBDISPATCH 297 __block 298#endif 299 RootlessWindowRec * winRec; 300 301 /* Stack frame below nextWid it if it exists, or raise 302 frame above everything otherwise. */ 303 304 if (nextWid == NULL) { 305 wc.stack_mode = XP_MAPPED_ABOVE; 306 wc.sibling = 0; 307 } 308 else { 309 wc.stack_mode = XP_MAPPED_BELOW; 310 wc.sibling = x_cvt_vptr_to_uint(nextWid); 311 } 312 313#ifdef HAVE_LIBDISPATCH 314 dispatch_sync(window_hash_serial_q, ^ { 315 winRec = x_hash_table_lookup(window_hash, wid, NULL); 316 }); 317#else 318 pthread_rwlock_rdlock(&window_hash_rwlock); 319 winRec = x_hash_table_lookup(window_hash, wid, NULL); 320 pthread_rwlock_unlock(&window_hash_rwlock); 321#endif 322 323 if (winRec) { 324 if (XQuartzIsRootless) 325 wc.window_level = normal_window_levels[winRec->level]; 326 else if (XQuartzShieldingWindowLevel) 327 wc.window_level = XQuartzShieldingWindowLevel + 1; 328 else 329 wc.window_level = rooted_window_levels[winRec->level]; 330 mask |= XP_WINDOW_LEVEL; 331 } 332 333 xprConfigureWindow(x_cvt_vptr_to_uint(wid), mask, &wc); 334} 335 336/* 337 * Change the frame's shape. 338 */ 339static void 340xprReshapeFrame(RootlessFrameID wid, RegionPtr pShape) 341{ 342 xp_window_changes wc; 343 344 if (pShape != NULL) { 345 wc.shape_nrects = RegionNumRects(pShape); 346 wc.shape_rects = RegionRects(pShape); 347 } 348 else { 349 wc.shape_nrects = -1; 350 wc.shape_rects = NULL; 351 } 352 353 wc.shape_tx = wc.shape_ty = 0; 354 355 xprConfigureWindow(x_cvt_vptr_to_uint(wid), XP_SHAPE, &wc); 356} 357 358/* 359 * Unmap a frame. 360 */ 361static void 362xprUnmapFrame(RootlessFrameID wid) 363{ 364 xp_window_changes wc; 365 366 wc.stack_mode = XP_UNMAPPED; 367 wc.sibling = 0; 368 369 xprConfigureWindow(x_cvt_vptr_to_uint(wid), XP_STACKING, &wc); 370} 371 372/* 373 * Start drawing to a frame. 374 * Prepare for direct access to its backing buffer. 375 */ 376static void 377xprStartDrawing(RootlessFrameID wid, char **pixelData, int *bytesPerRow) 378{ 379 void *data[2]; 380 unsigned int rowbytes[2]; 381 xp_error err; 382 383#ifdef DEBUG_XP_LOCK_WINDOW 384 void* callstack[128]; 385 int i, frames = backtrace(callstack, 128); 386 char** strs = backtrace_symbols(callstack, frames); 387 388 ErrorF("=== LOCK %d ===\n", (int)x_cvt_vptr_to_uint(wid)); 389 for (i = 0; i < frames; ++i) { 390 ErrorF(" %s\n", strs[i]); 391 } 392 free(strs); 393#endif 394 395 err = xp_lock_window(x_cvt_vptr_to_uint( 396 wid), NULL, NULL, data, rowbytes, NULL); 397 if (err != Success) 398 FatalError("Could not lock window %d for drawing (%d).", 399 (int)x_cvt_vptr_to_uint( 400 wid), (int)err); 401 402 *pixelData = data[0]; 403 *bytesPerRow = rowbytes[0]; 404} 405 406/* 407 * Stop drawing to a frame. 408 */ 409static void 410xprStopDrawing(RootlessFrameID wid, Bool flush) 411{ 412 xp_error err; 413 414#ifdef DEBUG_XP_LOCK_WINDOW 415 void* callstack[128]; 416 int i, frames = backtrace(callstack, 128); 417 char** strs = backtrace_symbols(callstack, frames); 418 419 ErrorF("=== UNLOCK %d ===\n", (int)x_cvt_vptr_to_uint(wid)); 420 for (i = 0; i < frames; ++i) { 421 ErrorF(" %s\n", strs[i]); 422 } 423 free(strs); 424#endif 425 426 err = xp_unlock_window(x_cvt_vptr_to_uint(wid), flush); 427 /* This should be a FatalError, but we started tripping over it. Make it a 428 * FatalError after http://xquartz.macosforge.org/trac/ticket/482 is fixed. 429 */ 430 if (err != Success) 431 ErrorF("Could not unlock window %d after drawing (%d).", 432 (int)x_cvt_vptr_to_uint( 433 wid), (int)err); 434} 435 436/* 437 * Flush drawing updates to the screen. 438 */ 439static void 440xprUpdateRegion(RootlessFrameID wid, RegionPtr pDamage) 441{ 442 xp_flush_window(x_cvt_vptr_to_uint(wid)); 443} 444 445/* 446 * Mark damaged rectangles as requiring redisplay to screen. 447 */ 448static void 449xprDamageRects(RootlessFrameID wid, int nrects, const BoxRec *rects, 450 int shift_x, int shift_y) 451{ 452 xp_mark_window(x_cvt_vptr_to_uint(wid), nrects, rects, shift_x, shift_y); 453} 454 455/* 456 * Called after the window associated with a frame has been switched 457 * to a new top-level parent. 458 */ 459static void 460xprSwitchWindow(RootlessWindowPtr pFrame, WindowPtr oldWin) 461{ 462 DeleteProperty(serverClient, oldWin, xa_native_window_id()); 463 464 xprSetNativeProperty(pFrame); 465} 466 467/* 468 * Called to check if the frame should be reordered when it is restacked. 469 */ 470static Bool 471xprDoReorderWindow(RootlessWindowPtr pFrame) 472{ 473 WindowPtr pWin = pFrame->win; 474 475 return AppleWMDoReorderWindow(pWin); 476} 477 478/* 479 * Copy area in frame to another part of frame. 480 * Used to accelerate scrolling. 481 */ 482static void 483xprCopyWindow(RootlessFrameID wid, int dstNrects, const BoxRec *dstRects, 484 int dx, int dy) 485{ 486 xp_copy_window(x_cvt_vptr_to_uint(wid), x_cvt_vptr_to_uint(wid), 487 dstNrects, dstRects, dx, dy); 488} 489 490static RootlessFrameProcsRec xprRootlessProcs = { 491 xprCreateFrame, 492 xprDestroyFrame, 493 xprMoveFrame, 494 xprResizeFrame, 495 xprRestackFrame, 496 xprReshapeFrame, 497 xprUnmapFrame, 498 xprStartDrawing, 499 xprStopDrawing, 500 xprUpdateRegion, 501 xprDamageRects, 502 xprSwitchWindow, 503 xprDoReorderWindow, 504 xprHideWindow, 505 xprUpdateColormap, 506 xp_copy_bytes, 507 xprCopyWindow 508}; 509 510/* 511 * Initialize XPR implementation 512 */ 513Bool 514xprInit(ScreenPtr pScreen) 515{ 516 RootlessInit(pScreen, &xprRootlessProcs); 517 518 rootless_CopyBytes_threshold = xp_copy_bytes_threshold; 519 rootless_CopyWindow_threshold = xp_scroll_area_threshold; 520 521 assert((window_hash = x_hash_table_new(NULL, NULL, NULL, NULL))); 522#ifdef HAVE_LIBDISPATCH 523 assert((window_hash_serial_q = 524 dispatch_queue_create(BUNDLE_ID_PREFIX ".X11.xpr_window_hash", 525 NULL))); 526#else 527 assert(0 == pthread_rwlock_init(&window_hash_rwlock, NULL)); 528#endif 529 530 return TRUE; 531} 532 533/* 534 * Given the id of a physical window, try to find the top-level (or root) 535 * X window that it represents. 536 */ 537WindowPtr 538xprGetXWindow(xp_window_id wid) 539{ 540#ifdef HAVE_LIBDISPATCH 541 RootlessWindowRec *winRec __block; 542 dispatch_sync(window_hash_serial_q, ^ { 543 winRec = 544 x_hash_table_lookup(window_hash, 545 x_cvt_uint_to_vptr(wid), NULL); 546 }); 547#else 548 RootlessWindowRec *winRec; 549 pthread_rwlock_rdlock(&window_hash_rwlock); 550 winRec = x_hash_table_lookup(window_hash, x_cvt_uint_to_vptr(wid), NULL); 551 pthread_rwlock_unlock(&window_hash_rwlock); 552#endif 553 554 return winRec != NULL ? winRec->win : NULL; 555} 556 557/* 558 * The windowNumber is an AppKit window number. Returns TRUE if xpr is 559 * displaying a window with that number. 560 */ 561Bool 562xprIsX11Window(int windowNumber) 563{ 564 Bool ret; 565 xp_window_id wid; 566 567 if (xp_lookup_native_window(windowNumber, &wid)) 568 ret = xprGetXWindow(wid) != NULL; 569 else 570 ret = FALSE; 571 572 return ret; 573} 574 575/* 576 * xprHideWindows 577 * Hide or unhide all top level windows. This is called for application hide/ 578 * unhide events if the window manager is not Apple-WM aware. Xplugin windows 579 * do not hide or unhide themselves. 580 */ 581void 582xprHideWindows(Bool hide) 583{ 584 int screen; 585 WindowPtr pRoot, pWin; 586 587 for (screen = 0; screen < screenInfo.numScreens; screen++) { 588 RootlessFrameID prevWid = NULL; 589 pRoot = screenInfo.screens[screen]->root; 590 591 for (pWin = pRoot->firstChild; pWin; pWin = pWin->nextSib) { 592 RootlessWindowRec *winRec = WINREC(pWin); 593 594 if (winRec != NULL) { 595 if (hide) { 596 xprUnmapFrame(winRec->wid); 597 } 598 else { 599 BoxRec box; 600 601 xprRestackFrame(winRec->wid, prevWid); 602 prevWid = winRec->wid; 603 604 box.x1 = 0; 605 box.y1 = 0; 606 box.x2 = winRec->width; 607 box.y2 = winRec->height; 608 609 xprDamageRects(winRec->wid, 1, &box, 0, 0); 610 RootlessQueueRedisplay(screenInfo.screens[screen]); 611 } 612 } 613 } 614 } 615} 616 617// XXX: identical to x_cvt_vptr_to_uint ? 618#define MAKE_WINDOW_ID(x) ((xp_window_id)((size_t)(x))) 619 620Bool no_configure_window; 621 622static inline int 623configure_window(xp_window_id id, unsigned int mask, 624 const xp_window_changes *values) 625{ 626 if (!no_configure_window) 627 return xp_configure_window(id, mask, values); 628 else 629 return XP_Success; 630} 631 632static 633void 634xprUpdateColormap(RootlessFrameID wid, ScreenPtr pScreen) 635{ 636 /* This is how we tell xp that the colormap may have changed. */ 637 xp_window_changes wc; 638 wc.colormap = xprColormapCallback; 639 wc.colormap_data = pScreen; 640 641 configure_window(MAKE_WINDOW_ID(wid), XP_COLORMAP, &wc); 642} 643 644static 645void 646xprHideWindow(RootlessFrameID wid) 647{ 648 xp_window_changes wc; 649 wc.stack_mode = XP_UNMAPPED; 650 wc.sibling = 0; 651 configure_window(MAKE_WINDOW_ID(wid), XP_STACKING, &wc); 652} 653