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