You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
xserver/hw/xquartz/xpr/xprFrame.c

614 lines
16 KiB
C

/*
* Xplugin rootless implementation frame functions
*
* Copyright (c) 2002-2012 Apple Computer, Inc. All rights reserved.
* Copyright (c) 2003 Torrey T. Lyons. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name(s) of the above copyright
* holders shall not be used in advertising or otherwise to promote the sale,
* use or other dealings in this Software without prior written authorization.
*/
#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif
#include "xpr.h"
#include "rootlessCommon.h"
#include <Xplugin.h>
#include "x-hash.h"
#include "applewmExt.h"
#include "propertyst.h"
#include "dix.h"
#include <X11/Xatom.h>
#include "windowstr.h"
#include "quartz.h"
#include <dispatch/dispatch.h>
#ifdef DEBUG_XP_LOCK_WINDOW
#include <execinfo.h>
#endif
#define DEFINE_ATOM_HELPER(func, atom_name) \
static Atom func(void) { \
static int generation; \
static Atom atom; \
if (generation != serverGeneration) { \
generation = serverGeneration; \
atom = MakeAtom(atom_name, strlen(atom_name), TRUE); \
} \
return atom; \
}
DEFINE_ATOM_HELPER(xa_native_window_id, "_NATIVE_WINDOW_ID")
/* Maps xp_window_id -> RootlessWindowRec */
static x_hash_table * window_hash;
/* Need to guard window_hash since xprIsX11Window can be called from any thread. */
static dispatch_queue_t window_hash_serial_q;
/* Prototypes for static functions */
static Bool
xprCreateFrame(RootlessWindowPtr pFrame, ScreenPtr pScreen, int newX,
int newY,
RegionPtr pShape);
static void
xprDestroyFrame(RootlessFrameID wid);
static void
xprMoveFrame(RootlessFrameID wid, ScreenPtr pScreen, int newX, int newY);
static void
xprResizeFrame(RootlessFrameID wid, ScreenPtr pScreen, int newX, int newY,
unsigned int newW, unsigned int newH,
unsigned int gravity);
static void
xprRestackFrame(RootlessFrameID wid, RootlessFrameID nextWid);
static void
xprReshapeFrame(RootlessFrameID wid, RegionPtr pShape);
static void
xprUnmapFrame(RootlessFrameID wid);
static void
xprStartDrawing(RootlessFrameID wid, char **pixelData, int *bytesPerRow);
static void
xprStopDrawing(RootlessFrameID wid, Bool flush);
static void
xprUpdateRegion(RootlessFrameID wid, RegionPtr pDamage);
static void
xprDamageRects(RootlessFrameID wid, int nrects, const BoxRec *rects,
int shift_x,
int shift_y);
static void
xprSwitchWindow(RootlessWindowPtr pFrame, WindowPtr oldWin);
static Bool
xprDoReorderWindow(RootlessWindowPtr pFrame);
static void
xprHideWindow(RootlessFrameID wid);
static void
xprUpdateColormap(RootlessFrameID wid, ScreenPtr pScreen);
static void
xprCopyWindow(RootlessFrameID wid, int dstNrects, const BoxRec *dstRects,
int dx,
int dy);
static inline xp_error
xprConfigureWindow(xp_window_id id, unsigned int mask,
const xp_window_changes *values)
{
return xp_configure_window(id, mask, values);
}
static void
xprSetNativeProperty(RootlessWindowPtr pFrame)
{
xp_error err;
unsigned int native_id;
long data;
err = xp_get_native_window(x_cvt_vptr_to_uint(pFrame->wid), &native_id);
if (err == Success) {
/* FIXME: move this to AppleWM extension */
data = native_id;
dixChangeWindowProperty(serverClient, pFrame->win,
xa_native_window_id(),
XA_INTEGER, 32, PropModeReplace, 1, &data,
TRUE);
}
}
static xp_error
xprColormapCallback(void *data, int first_color, int n_colors,
uint32_t *colors)
{
return (RootlessResolveColormap(data, first_color, n_colors,
colors) ? XP_Success : XP_BadMatch);
}
/*
* Create and display a new frame.
*/
static Bool
xprCreateFrame(RootlessWindowPtr pFrame, ScreenPtr pScreen,
int newX, int newY, RegionPtr pShape)
{
WindowPtr pWin = pFrame->win;
xp_window_changes wc;
unsigned int mask = 0;
xp_error err;
wc.x = newX;
wc.y = newY;
wc.width = pFrame->width;
wc.height = pFrame->height;
wc.bit_gravity = XP_GRAVITY_NONE;
mask |= XP_BOUNDS;
if (pWin->drawable.depth == 8) {
wc.depth = XP_DEPTH_INDEX8;
wc.colormap = xprColormapCallback;
wc.colormap_data = pScreen;
mask |= XP_COLORMAP;
}
else if (pWin->drawable.depth == 15)
wc.depth = XP_DEPTH_RGB555;
else if (pWin->drawable.depth == 24)
wc.depth = XP_DEPTH_ARGB8888;
else
wc.depth = XP_DEPTH_NIL;
mask |= XP_DEPTH;
if (pShape != NULL) {
wc.shape_nrects = RegionNumRects(pShape);
wc.shape_rects = RegionRects(pShape);
wc.shape_tx = wc.shape_ty = 0;
mask |= XP_SHAPE;
}
pFrame->level =
!IsRoot(pWin) ? AppleWMWindowLevelNormal : AppleWMNumWindowLevels;
if (XQuartzIsRootless)
wc.window_level = normal_window_levels[pFrame->level];
else if (XQuartzShieldingWindowLevel)
wc.window_level = XQuartzShieldingWindowLevel + 1;
else
wc.window_level = rooted_window_levels[pFrame->level];
mask |= XP_WINDOW_LEVEL;
err = xp_create_window(mask, &wc, (xp_window_id *)&pFrame->wid);
if (err != Success) {
return FALSE;
}
dispatch_async(window_hash_serial_q, ^ {
x_hash_table_insert(window_hash, pFrame->wid, pFrame);
});
xprSetNativeProperty(pFrame);
return TRUE;
}
/*
* Destroy a frame.
*/
static void
xprDestroyFrame(RootlessFrameID wid)
{
xp_error err;
dispatch_async(window_hash_serial_q, ^ {
x_hash_table_remove(window_hash, wid);
});
err = xp_destroy_window(x_cvt_vptr_to_uint(wid));
if (err != Success)
FatalError("Could not destroy window %d (%d).",
(int)x_cvt_vptr_to_uint(
wid), (int)err);
}
/*
* Move a frame on screen.
*/
static void
xprMoveFrame(RootlessFrameID wid, ScreenPtr pScreen, int newX, int newY)
{
xp_window_changes wc;
wc.x = newX;
wc.y = newY;
// ErrorF("xprMoveFrame(%d, %p, %d, %d)\n", wid, pScreen, newX, newY);
xprConfigureWindow(x_cvt_vptr_to_uint(wid), XP_ORIGIN, &wc);
}
/*
* Resize and move a frame.
*/
static void
xprResizeFrame(RootlessFrameID wid, ScreenPtr pScreen,
int newX, int newY, unsigned int newW, unsigned int newH,
unsigned int gravity)
{
xp_window_changes wc;
wc.x = newX;
wc.y = newY;
wc.width = newW;
wc.height = newH;
wc.bit_gravity = gravity;
/* It's unlikely that being async will save us anything here.
But it can't hurt. */
xprConfigureWindow(x_cvt_vptr_to_uint(wid), XP_BOUNDS, &wc);
}
/*
* Change frame stacking.
*/
static void
xprRestackFrame(RootlessFrameID wid, RootlessFrameID nextWid)
{
xp_window_changes wc;
unsigned int mask = XP_STACKING;
__block
RootlessWindowRec * winRec;
/* Stack frame below nextWid it if it exists, or raise
frame above everything otherwise. */
if (nextWid == NULL) {
wc.stack_mode = XP_MAPPED_ABOVE;
wc.sibling = 0;
}
else {
wc.stack_mode = XP_MAPPED_BELOW;
wc.sibling = x_cvt_vptr_to_uint(nextWid);
}
dispatch_sync(window_hash_serial_q, ^ {
winRec = x_hash_table_lookup(window_hash, wid, NULL);
});
if (winRec) {
if (XQuartzIsRootless)
wc.window_level = normal_window_levels[winRec->level];
else if (XQuartzShieldingWindowLevel)
wc.window_level = XQuartzShieldingWindowLevel + 1;
else
wc.window_level = rooted_window_levels[winRec->level];
mask |= XP_WINDOW_LEVEL;
}
xprConfigureWindow(x_cvt_vptr_to_uint(wid), mask, &wc);
}
/*
* Change the frame's shape.
*/
static void
xprReshapeFrame(RootlessFrameID wid, RegionPtr pShape)
{
xp_window_changes wc;
if (pShape != NULL) {
wc.shape_nrects = RegionNumRects(pShape);
wc.shape_rects = RegionRects(pShape);
}
else {
wc.shape_nrects = -1;
wc.shape_rects = NULL;
}
wc.shape_tx = wc.shape_ty = 0;
xprConfigureWindow(x_cvt_vptr_to_uint(wid), XP_SHAPE, &wc);
}
/*
* Unmap a frame.
*/
static void
xprUnmapFrame(RootlessFrameID wid)
{
xp_window_changes wc;
wc.stack_mode = XP_UNMAPPED;
wc.sibling = 0;
xprConfigureWindow(x_cvt_vptr_to_uint(wid), XP_STACKING, &wc);
}
/*
* Start drawing to a frame.
* Prepare for direct access to its backing buffer.
*/
static void
xprStartDrawing(RootlessFrameID wid, char **pixelData, int *bytesPerRow)
{
void *data[2];
unsigned int rowbytes[2];
xp_error err;
#ifdef DEBUG_XP_LOCK_WINDOW
void* callstack[128];
int i, frames = backtrace(callstack, 128);
char** strs = backtrace_symbols(callstack, frames);
ErrorF("=== LOCK %d ===\n", (int)x_cvt_vptr_to_uint(wid));
for (i = 0; i < frames; ++i) {
ErrorF(" %s\n", strs[i]);
}
free(strs);
#endif
err = xp_lock_window(x_cvt_vptr_to_uint(
wid), NULL, NULL, data, rowbytes, NULL);
if (err != Success)
FatalError("Could not lock window %d for drawing (%d).",
(int)x_cvt_vptr_to_uint(
wid), (int)err);
*pixelData = data[0];
*bytesPerRow = rowbytes[0];
}
/*
* Stop drawing to a frame.
*/
static void
xprStopDrawing(RootlessFrameID wid, Bool flush)
{
xp_error err;
#ifdef DEBUG_XP_LOCK_WINDOW
void* callstack[128];
int i, frames = backtrace(callstack, 128);
char** strs = backtrace_symbols(callstack, frames);
ErrorF("=== UNLOCK %d ===\n", (int)x_cvt_vptr_to_uint(wid));
for (i = 0; i < frames; ++i) {
ErrorF(" %s\n", strs[i]);
}
free(strs);
#endif
err = xp_unlock_window(x_cvt_vptr_to_uint(wid), flush);
/* This should be a FatalError, but we started tripping over it. Make it a
* FatalError after http://xquartz.macosforge.org/trac/ticket/482 is fixed.
*/
if (err != Success)
ErrorF("Could not unlock window %d after drawing (%d).",
(int)x_cvt_vptr_to_uint(
wid), (int)err);
}
/*
* Flush drawing updates to the screen.
*/
static void
xprUpdateRegion(RootlessFrameID wid, RegionPtr pDamage)
{
xp_flush_window(x_cvt_vptr_to_uint(wid));
}
/*
* Mark damaged rectangles as requiring redisplay to screen.
*/
static void
xprDamageRects(RootlessFrameID wid, int nrects, const BoxRec *rects,
int shift_x, int shift_y)
{
xp_mark_window(x_cvt_vptr_to_uint(wid), nrects, rects, shift_x, shift_y);
}
/*
* Called after the window associated with a frame has been switched
* to a new top-level parent.
*/
static void
xprSwitchWindow(RootlessWindowPtr pFrame, WindowPtr oldWin)
{
DeleteProperty(serverClient, oldWin, xa_native_window_id());
xprSetNativeProperty(pFrame);
}
/*
* Called to check if the frame should be reordered when it is restacked.
*/
static Bool
xprDoReorderWindow(RootlessWindowPtr pFrame)
{
WindowPtr pWin = pFrame->win;
return AppleWMDoReorderWindow(pWin);
}
/*
* Copy area in frame to another part of frame.
* Used to accelerate scrolling.
*/
static void
xprCopyWindow(RootlessFrameID wid, int dstNrects, const BoxRec *dstRects,
int dx, int dy)
{
xp_copy_window(x_cvt_vptr_to_uint(wid), x_cvt_vptr_to_uint(wid),
dstNrects, dstRects, dx, dy);
}
static RootlessFrameProcsRec xprRootlessProcs = {
xprCreateFrame,
xprDestroyFrame,
xprMoveFrame,
xprResizeFrame,
xprRestackFrame,
xprReshapeFrame,
xprUnmapFrame,
xprStartDrawing,
xprStopDrawing,
xprUpdateRegion,
xprDamageRects,
xprSwitchWindow,
xprDoReorderWindow,
xprHideWindow,
xprUpdateColormap,
xp_copy_bytes,
xprCopyWindow
};
/*
* Initialize XPR implementation
*/
Bool
xprInit(ScreenPtr pScreen)
{
RootlessInit(pScreen, &xprRootlessProcs);
rootless_CopyBytes_threshold = xp_copy_bytes_threshold;
rootless_CopyWindow_threshold = xp_scroll_area_threshold;
assert((window_hash = x_hash_table_new(NULL, NULL, NULL, NULL)));
assert((window_hash_serial_q =
dispatch_queue_create(BUNDLE_ID_PREFIX ".X11.xpr_window_hash",
NULL)));
return TRUE;
}
/*
* Given the id of a physical window, try to find the top-level (or root)
* X window that it represents.
*/
WindowPtr
xprGetXWindow(xp_window_id wid)
{
RootlessWindowRec *winRec __block;
dispatch_sync(window_hash_serial_q, ^ {
winRec =
x_hash_table_lookup(window_hash,
x_cvt_uint_to_vptr(wid), NULL);
});
return winRec != NULL ? winRec->win : NULL;
}
/*
* The windowNumber is an AppKit window number. Returns TRUE if xpr is
* displaying a window with that number.
*/
Bool
xprIsX11Window(int windowNumber)
{
Bool ret;
xp_window_id wid;
if (xp_lookup_native_window(windowNumber, &wid))
ret = xprGetXWindow(wid) != NULL;
else
ret = FALSE;
return ret;
}
/*
* xprHideWindows
* Hide or unhide all top level windows. This is called for application hide/
* unhide events if the window manager is not Apple-WM aware. Xplugin windows
* do not hide or unhide themselves.
*/
void
xprHideWindows(Bool hide)
{
int screen;
WindowPtr pRoot, pWin;
for (screen = 0; screen < screenInfo.numScreens; screen++) {
RootlessFrameID prevWid = NULL;
pRoot = screenInfo.screens[screen]->root;
for (pWin = pRoot->firstChild; pWin; pWin = pWin->nextSib) {
RootlessWindowRec *winRec = WINREC(pWin);
if (winRec != NULL) {
if (hide) {
xprUnmapFrame(winRec->wid);
}
else {
BoxRec box;
xprRestackFrame(winRec->wid, prevWid);
prevWid = winRec->wid;
box.x1 = 0;
box.y1 = 0;
box.x2 = winRec->width;
box.y2 = winRec->height;
xprDamageRects(winRec->wid, 1, &box, 0, 0);
RootlessQueueRedisplay(screenInfo.screens[screen]);
}
}
}
}
}
// XXX: identical to x_cvt_vptr_to_uint ?
#define MAKE_WINDOW_ID(x) ((xp_window_id)((size_t)(x)))
Bool no_configure_window;
static inline int
configure_window(xp_window_id id, unsigned int mask,
const xp_window_changes *values)
{
if (!no_configure_window)
return xp_configure_window(id, mask, values);
else
return XP_Success;
}
static
void
xprUpdateColormap(RootlessFrameID wid, ScreenPtr pScreen)
{
/* This is how we tell xp that the colormap may have changed. */
xp_window_changes wc;
wc.colormap = xprColormapCallback;
wc.colormap_data = pScreen;
configure_window(MAKE_WINDOW_ID(wid), XP_COLORMAP, &wc);
}
static
void
xprHideWindow(RootlessFrameID wid)
{
xp_window_changes wc;
wc.stack_mode = XP_UNMAPPED;
wc.sibling = 0;
configure_window(MAKE_WINDOW_ID(wid), XP_STACKING, &wc);
}