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