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 59DEFINE_ATOM_HELPER(xa_native_window_id, "_NATIVE_WINDOW_ID") 60 61/* Maps xp_window_id -> RootlessWindowRec */ 62static x_hash_table * window_hash; 63 64/* Need to guard window_hash since xprIsX11Window can be called from any thread. */ 65static dispatch_queue_t window_hash_serial_q; 66 67/* Prototypes for static functions */ 68static Bool 69xprCreateFrame(RootlessWindowPtr pFrame, ScreenPtr pScreen, int newX, 70 int newY, 71 RegionPtr pShape); 72static void 73xprDestroyFrame(RootlessFrameID wid); 74static void 75xprMoveFrame(RootlessFrameID wid, ScreenPtr pScreen, int newX, int newY); 76static void 77xprResizeFrame(RootlessFrameID wid, ScreenPtr pScreen, int newX, int newY, 78 unsigned int newW, unsigned int newH, 79 unsigned int gravity); 80static void 81xprRestackFrame(RootlessFrameID wid, RootlessFrameID nextWid); 82static void 83xprReshapeFrame(RootlessFrameID wid, RegionPtr pShape); 84static void 85xprUnmapFrame(RootlessFrameID wid); 86static void 87xprStartDrawing(RootlessFrameID wid, char **pixelData, int *bytesPerRow); 88static void 89xprStopDrawing(RootlessFrameID wid, Bool flush); 90static void 91xprUpdateRegion(RootlessFrameID wid, RegionPtr pDamage); 92static void 93xprDamageRects(RootlessFrameID wid, int nrects, const BoxRec *rects, 94 int shift_x, 95 int shift_y); 96static void 97xprSwitchWindow(RootlessWindowPtr pFrame, WindowPtr oldWin); 98static Bool 99xprDoReorderWindow(RootlessWindowPtr pFrame); 100static void 101xprHideWindow(RootlessFrameID wid); 102static void 103xprUpdateColormap(RootlessFrameID wid, ScreenPtr pScreen); 104static void 105xprCopyWindow(RootlessFrameID wid, int dstNrects, const BoxRec *dstRects, 106 int dx, 107 int dy); 108 109static inline xp_error 110xprConfigureWindow(xp_window_id id, unsigned int mask, 111 const xp_window_changes *values) 112{ 113 return xp_configure_window(id, mask, values); 114} 115 116static void 117xprSetNativeProperty(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 135static xp_error 136xprColormapCallback(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 */ 146static Bool 147xprCreateFrame(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 */ 212static void 213xprDestroyFrame(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 */ 231static void 232xprMoveFrame(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 */ 245static void 246xprResizeFrame(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 */ 267static void 268xprRestackFrame(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 */ 307static void 308xprReshapeFrame(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 */ 329static void 330xprUnmapFrame(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 */ 344static void 345xprStartDrawing(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 */ 374static void 375xprStopDrawing(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 */ 397static void 398xprUpdateRegion(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 */ 406static void 407xprDamageRects(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 */ 417static void 418xprSwitchWindow(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 */ 428static Bool 429xprDoReorderWindow(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 */ 440static void 441xprCopyWindow(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 448static 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 */ 471Bool 472xprInit(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 */ 491WindowPtr 492xprGetXWindow(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 */ 508Bool 509xprIsX11Window(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 */ 528void 529xprHideWindows(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 567Bool no_configure_window; 568 569static inline int 570configure_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 579static 580void 581xprUpdateColormap(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 591static 592void 593xprHideWindow(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