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