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 
63 DEFINE_ATOM_HELPER(xa_native_window_id, "_NATIVE_WINDOW_ID")
64 
65 /* Maps xp_window_id -> RootlessWindowRec */
66 static x_hash_table * window_hash;
67 
68 /* Need to guard window_hash since xprIsX11Window can be called from any thread. */
69 static dispatch_queue_t window_hash_serial_q;
70 
71 /* Prototypes for static functions */
72 static Bool
73 xprCreateFrame(RootlessWindowPtr pFrame, ScreenPtr pScreen, int newX,
74                int newY,
75                RegionPtr pShape);
76 static void
77 xprDestroyFrame(RootlessFrameID wid);
78 static void
79 xprMoveFrame(RootlessFrameID wid, ScreenPtr pScreen, int newX, int newY);
80 static void
81 xprResizeFrame(RootlessFrameID wid, ScreenPtr pScreen, int newX, int newY,
82                unsigned int newW, unsigned int newH,
83                unsigned int gravity);
84 static void
85 xprRestackFrame(RootlessFrameID wid, RootlessFrameID nextWid);
86 static void
87 xprReshapeFrame(RootlessFrameID wid, RegionPtr pShape);
88 static void
89 xprUnmapFrame(RootlessFrameID wid);
90 static void
91 xprStartDrawing(RootlessFrameID wid, char **pixelData, int *bytesPerRow);
92 static void
93 xprStopDrawing(RootlessFrameID wid, Bool flush);
94 static void
95 xprUpdateRegion(RootlessFrameID wid, RegionPtr pDamage);
96 static void
97 xprDamageRects(RootlessFrameID wid, int nrects, const BoxRec *rects,
98                int shift_x,
99                int shift_y);
100 static void
101 xprSwitchWindow(RootlessWindowPtr pFrame, WindowPtr oldWin);
102 static Bool
103 xprDoReorderWindow(RootlessWindowPtr pFrame);
104 static void
105 xprHideWindow(RootlessFrameID wid);
106 static void
107 xprUpdateColormap(RootlessFrameID wid, ScreenPtr pScreen);
108 static void
109 xprCopyWindow(RootlessFrameID wid, int dstNrects, const BoxRec *dstRects,
110               int dx,
111               int dy);
112 
113 static inline xp_error
xprConfigureWindow(xp_window_id id,unsigned int mask,const xp_window_changes * values)114 xprConfigureWindow(xp_window_id id, unsigned int mask,
115                    const xp_window_changes *values)
116 {
117     return xp_configure_window(id, mask, values);
118 }
119 
120 static void
xprSetNativeProperty(RootlessWindowPtr pFrame)121 xprSetNativeProperty(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 
139 static xp_error
xprColormapCallback(void * data,int first_color,int n_colors,uint32_t * colors)140 xprColormapCallback(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  */
150 static Bool
xprCreateFrame(RootlessWindowPtr pFrame,ScreenPtr pScreen,int newX,int newY,RegionPtr pShape)151 xprCreateFrame(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  */
216 static void
xprDestroyFrame(RootlessFrameID wid)217 xprDestroyFrame(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  */
235 static void
xprMoveFrame(RootlessFrameID wid,ScreenPtr pScreen,int newX,int newY)236 xprMoveFrame(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  */
249 static void
xprResizeFrame(RootlessFrameID wid,ScreenPtr pScreen,int newX,int newY,unsigned int newW,unsigned int newH,unsigned int gravity)250 xprResizeFrame(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  */
271 static void
xprRestackFrame(RootlessFrameID wid,RootlessFrameID nextWid)272 xprRestackFrame(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  */
311 static void
xprReshapeFrame(RootlessFrameID wid,RegionPtr pShape)312 xprReshapeFrame(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  */
333 static void
xprUnmapFrame(RootlessFrameID wid)334 xprUnmapFrame(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  */
348 static void
xprStartDrawing(RootlessFrameID wid,char ** pixelData,int * bytesPerRow)349 xprStartDrawing(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  */
381 static void
xprStopDrawing(RootlessFrameID wid,Bool flush)382 xprStopDrawing(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  */
411 static void
xprUpdateRegion(RootlessFrameID wid,RegionPtr pDamage)412 xprUpdateRegion(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  */
420 static void
xprDamageRects(RootlessFrameID wid,int nrects,const BoxRec * rects,int shift_x,int shift_y)421 xprDamageRects(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  */
431 static void
xprSwitchWindow(RootlessWindowPtr pFrame,WindowPtr oldWin)432 xprSwitchWindow(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  */
442 static Bool
xprDoReorderWindow(RootlessWindowPtr pFrame)443 xprDoReorderWindow(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  */
454 static void
xprCopyWindow(RootlessFrameID wid,int dstNrects,const BoxRec * dstRects,int dx,int dy)455 xprCopyWindow(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 
462 static 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  */
485 Bool
xprInit(ScreenPtr pScreen)486 xprInit(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  */
505 WindowPtr
xprGetXWindow(xp_window_id wid)506 xprGetXWindow(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  */
522 Bool
xprIsX11Window(int windowNumber)523 xprIsX11Window(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  */
542 void
xprHideWindows(Bool hide)543 xprHideWindows(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 
581 Bool no_configure_window;
582 
583 static inline int
configure_window(xp_window_id id,unsigned int mask,const xp_window_changes * values)584 configure_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 
593 static
594 void
xprUpdateColormap(RootlessFrameID wid,ScreenPtr pScreen)595 xprUpdateColormap(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 
605 static
606 void
xprHideWindow(RootlessFrameID wid)607 xprHideWindow(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