Home | History | Annotate | Line # | Download | only in xpr
      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