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