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