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