Imported Upstream version 1.15.1
[deb_xorg-server.git] / hw / xquartz / xpr / xprFrame.c
CommitLineData
a09e091a
JB
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}