439 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Objective-C
		
	
	
	
			
		
		
	
	
			439 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Objective-C
		
	
	
	
/*
 | 
						|
 * Cocoa rootless implementation frame functions
 | 
						|
 *
 | 
						|
 * Copyright (c) 2001 Greg Parker. All Rights Reserved.
 | 
						|
 * Copyright (c) 2002-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.
 | 
						|
 */
 | 
						|
 | 
						|
#include <dix-config.h>
 | 
						|
 | 
						|
#include "quartz/quartzCommon.h"
 | 
						|
#include "quartz/cr/cr.h"
 | 
						|
 | 
						|
#undef BOOL
 | 
						|
#define BOOL xBOOL
 | 
						|
#include "rootless.h"
 | 
						|
#include "quartz/applewmExt.h"
 | 
						|
#include "windowstr.h"
 | 
						|
#undef BOOL
 | 
						|
 | 
						|
WindowPtr nextWindowToFrame = NULL;
 | 
						|
unsigned int nextWindowStyle = 0;
 | 
						|
 | 
						|
static void CRReshapeFrame(RootlessFrameID wid, RegionPtr pShape);
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * CRCreateFrame
 | 
						|
 *  Create a new physical window.
 | 
						|
 *  Rootless windows must not autodisplay! Autodisplay can cause a deadlock.
 | 
						|
 *    Event thread - autodisplay: locks view hierarchy, then window
 | 
						|
 *    X Server thread - window resize: locks window, then view hierarchy
 | 
						|
 *  Deadlock occurs if each thread gets one lock and waits for the other.
 | 
						|
 */
 | 
						|
static Bool
 | 
						|
CRCreateFrame(RootlessWindowPtr pFrame, ScreenPtr pScreen,
 | 
						|
              int newX, int newY, RegionPtr pShape)
 | 
						|
{
 | 
						|
    CRWindowPtr crWinPtr;
 | 
						|
    NSRect bounds;
 | 
						|
    NSWindow *theWindow;
 | 
						|
    XView *theView;
 | 
						|
    unsigned int theStyleMask = NSBorderlessWindowMask;
 | 
						|
 | 
						|
    crWinPtr = (CRWindowPtr) xalloc(sizeof(CRWindowRec));
 | 
						|
 | 
						|
    bounds = NSMakeRect(newX,
 | 
						|
                        NSHeight([[NSScreen mainScreen] frame]) -
 | 
						|
                        newY - pFrame->height,
 | 
						|
                        pFrame->width, pFrame->height);
 | 
						|
 | 
						|
    // Check if AppleWM has specified a style for this window
 | 
						|
    if (pFrame->win == nextWindowToFrame) {
 | 
						|
        theStyleMask = nextWindowStyle;
 | 
						|
    }
 | 
						|
    nextWindowToFrame = NULL;
 | 
						|
 | 
						|
    // Create an NSWindow for the new X11 window
 | 
						|
    theWindow = [[NSWindow alloc] initWithContentRect:bounds
 | 
						|
                                  styleMask:theStyleMask
 | 
						|
                                  backing:NSBackingStoreBuffered
 | 
						|
#ifdef DEFER_NSWINDOW
 | 
						|
                                  defer:YES];
 | 
						|
#else
 | 
						|
                                  defer:NO];
 | 
						|
#endif
 | 
						|
 | 
						|
    if (!theWindow) return FALSE;
 | 
						|
 | 
						|
    [theWindow setBackgroundColor:[NSColor clearColor]];  // erase transparent
 | 
						|
    [theWindow setAlphaValue:1.0];       // draw opaque
 | 
						|
    [theWindow setOpaque:YES];           // changed when window is shaped
 | 
						|
 | 
						|
    [theWindow useOptimizedDrawing:YES]; // Has no overlapping sub-views
 | 
						|
    [theWindow setAutodisplay:NO];       // See comment above
 | 
						|
    [theWindow disableFlushWindow];      // We do all the flushing manually
 | 
						|
    [theWindow setHasShadow:YES];        // All windows have shadows
 | 
						|
    [theWindow setReleasedWhenClosed:YES]; // Default, but we want to be sure
 | 
						|
 | 
						|
    theView = [[XView alloc] initWithFrame:bounds];
 | 
						|
    [theWindow setContentView:theView];
 | 
						|
    [theWindow setInitialFirstResponder:theView];
 | 
						|
 | 
						|
#ifdef DEFER_NSWINDOW
 | 
						|
    // We need the NSWindow to actually be created now.
 | 
						|
    // If we had to defer creating it, we have to order it
 | 
						|
    // onto the screen to force it to be created.
 | 
						|
 | 
						|
    if (pFrame->win->prevSib) {
 | 
						|
        CRWindowPtr crWinPtr = (CRWindowPtr) RootlessFrameForWindow(
 | 
						|
                                                pFrame->win->prevSib, FALSE);
 | 
						|
        int upperNum = [crWinPtr->window windowNumber];
 | 
						|
        [theWindow orderWindow:NSWindowBelow relativeTo:upperNum];
 | 
						|
    } else {
 | 
						|
        [theWindow orderFront:nil];
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    [theWindow setAcceptsMouseMovedEvents:YES];
 | 
						|
 | 
						|
    crWinPtr->window = theWindow;
 | 
						|
    crWinPtr->view = theView;
 | 
						|
 | 
						|
    [theView lockFocus];
 | 
						|
    // Fill the window with white to make sure alpha channel is set
 | 
						|
    NSEraseRect(bounds);
 | 
						|
    crWinPtr->port = [theView qdPort];
 | 
						|
    crWinPtr->context = [[NSGraphicsContext currentContext] graphicsPort];
 | 
						|
    // CreateCGContextForPort(crWinPtr->port, &crWinPtr->context);
 | 
						|
    [theView unlockFocus];
 | 
						|
 | 
						|
    // Store the implementation private frame ID
 | 
						|
    pFrame->wid = (RootlessFrameID) crWinPtr;
 | 
						|
 | 
						|
    // Reshape the frame if it was created shaped.
 | 
						|
    if (pShape != NULL)
 | 
						|
        CRReshapeFrame(pFrame->wid, pShape);
 | 
						|
 | 
						|
    return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * CRDestroyFrame
 | 
						|
 *  Destroy a frame.
 | 
						|
 */
 | 
						|
static void
 | 
						|
CRDestroyFrame(RootlessFrameID wid)
 | 
						|
{
 | 
						|
    CRWindowPtr crWinPtr = (CRWindowPtr) wid;
 | 
						|
 | 
						|
    [crWinPtr->window orderOut:nil];
 | 
						|
    [crWinPtr->window close];
 | 
						|
    [crWinPtr->view release];
 | 
						|
    free(crWinPtr);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * CRMoveFrame
 | 
						|
 *  Move a frame on screen.
 | 
						|
 */
 | 
						|
static void
 | 
						|
CRMoveFrame(RootlessFrameID wid, ScreenPtr pScreen, int newX, int newY)
 | 
						|
{
 | 
						|
    CRWindowPtr crWinPtr = (CRWindowPtr) wid;
 | 
						|
    NSPoint topLeft;
 | 
						|
 | 
						|
    topLeft = NSMakePoint(newX,
 | 
						|
                          NSHeight([[NSScreen mainScreen] frame]) - newY);
 | 
						|
 | 
						|
    [crWinPtr->window setFrameTopLeftPoint:topLeft];
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * CRResizeFrame
 | 
						|
 *  Move and resize a frame.
 | 
						|
 */
 | 
						|
static void
 | 
						|
CRResizeFrame(RootlessFrameID wid, ScreenPtr pScreen,
 | 
						|
              int newX, int newY, unsigned int newW, unsigned int newH,
 | 
						|
              unsigned int gravity)
 | 
						|
{
 | 
						|
    CRWindowPtr crWinPtr = (CRWindowPtr) wid;
 | 
						|
    NSRect bounds = NSMakeRect(newX, NSHeight([[NSScreen mainScreen] frame]) -
 | 
						|
                               newY - newH, newW, newH);
 | 
						|
 | 
						|
    [crWinPtr->window setFrame:bounds display:NO];
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * CRRestackFrame
 | 
						|
 *  Change the frame order. Put the frame behind nextWid or on top if
 | 
						|
 *  it is NULL. Unmapped frames are mapped by restacking them.
 | 
						|
 */
 | 
						|
static void
 | 
						|
CRRestackFrame(RootlessFrameID wid, RootlessFrameID nextWid)
 | 
						|
{
 | 
						|
    CRWindowPtr crWinPtr = (CRWindowPtr) wid;
 | 
						|
    CRWindowPtr crNextWinPtr = (CRWindowPtr) nextWid;
 | 
						|
 | 
						|
    if (crNextWinPtr) {
 | 
						|
        int upperNum = [crNextWinPtr->window windowNumber];
 | 
						|
 | 
						|
        [crWinPtr->window orderWindow:NSWindowBelow relativeTo:upperNum];
 | 
						|
    } else {
 | 
						|
        [crWinPtr->window makeKeyAndOrderFront:nil];
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * CRReshapeFrame
 | 
						|
 *  Set the shape of a frame.
 | 
						|
 */
 | 
						|
static void
 | 
						|
CRReshapeFrame(RootlessFrameID wid, RegionPtr pShape)
 | 
						|
{
 | 
						|
    CRWindowPtr crWinPtr = (CRWindowPtr) wid;
 | 
						|
    NSRect bounds = [crWinPtr->view frame];
 | 
						|
    int winHeight = NSHeight(bounds);
 | 
						|
    BoxRec localBox = {0, 0, NSWidth(bounds), winHeight};
 | 
						|
 | 
						|
    [crWinPtr->view lockFocus];
 | 
						|
 | 
						|
    if (pShape != NULL) {
 | 
						|
        // Calculate the region outside the new shape.
 | 
						|
        miInverse(pShape, pShape, &localBox);
 | 
						|
    }
 | 
						|
 | 
						|
    // If window is currently shaped we need to undo the previous shape.
 | 
						|
    if (![crWinPtr->window isOpaque]) {
 | 
						|
        [[NSColor whiteColor] set];
 | 
						|
        NSRectFillUsingOperation(bounds, NSCompositeDestinationAtop);
 | 
						|
    }
 | 
						|
 | 
						|
    if (pShape != NULL) {
 | 
						|
        int count = REGION_NUM_RECTS(pShape);
 | 
						|
        BoxRec *extRects = REGION_RECTS(pShape);
 | 
						|
        BoxRec *rects, *end;
 | 
						|
 | 
						|
        // Make transparent if window is now shaped.
 | 
						|
        [crWinPtr->window setOpaque:NO];
 | 
						|
 | 
						|
        // Clear the areas outside the window shape
 | 
						|
        [[NSColor clearColor] set];
 | 
						|
        for (rects = extRects, end = extRects+count; rects < end; rects++) {
 | 
						|
            int rectHeight = rects->y2 - rects->y1;
 | 
						|
            NSRectFill( NSMakeRect(rects->x1,
 | 
						|
                                   winHeight - rects->y1 - rectHeight,
 | 
						|
                                   rects->x2 - rects->x1, rectHeight) );
 | 
						|
        }
 | 
						|
        [[NSGraphicsContext currentContext] flushGraphics];
 | 
						|
 | 
						|
        // force update of window shadow
 | 
						|
        [crWinPtr->window setHasShadow:NO];
 | 
						|
        [crWinPtr->window setHasShadow:YES];
 | 
						|
 | 
						|
    } else {
 | 
						|
        [crWinPtr->window setOpaque:YES];
 | 
						|
        [[NSGraphicsContext currentContext] flushGraphics];
 | 
						|
    }
 | 
						|
 | 
						|
    [crWinPtr->view unlockFocus];
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * CRUnmapFrame
 | 
						|
 *  Unmap a frame.
 | 
						|
 */
 | 
						|
static void
 | 
						|
CRUnmapFrame(RootlessFrameID wid)
 | 
						|
{
 | 
						|
    CRWindowPtr crWinPtr = (CRWindowPtr) wid;
 | 
						|
 | 
						|
    [crWinPtr->window orderOut:nil];
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * CRStartDrawing
 | 
						|
 *  When a window's buffer is not being drawn to, the CoreGraphics
 | 
						|
 *  window server may compress or move it. Call this routine
 | 
						|
 *  to lock down the buffer during direct drawing. It returns
 | 
						|
 *  a pointer to the backing buffer.
 | 
						|
 */
 | 
						|
static void
 | 
						|
CRStartDrawing(RootlessFrameID wid, char **pixelData, int *bytesPerRow)
 | 
						|
{
 | 
						|
    CRWindowPtr crWinPtr = (CRWindowPtr) wid;
 | 
						|
    PixMapHandle pix;
 | 
						|
 | 
						|
    [crWinPtr->view lockFocus];
 | 
						|
    crWinPtr->port = [crWinPtr->view qdPort];
 | 
						|
    LockPortBits(crWinPtr->port);
 | 
						|
    [crWinPtr->view unlockFocus];
 | 
						|
    pix = GetPortPixMap(crWinPtr->port);
 | 
						|
 | 
						|
    *pixelData = GetPixBaseAddr(pix);
 | 
						|
    *bytesPerRow = GetPixRowBytes(pix) & 0x3fff; // fixme is mask needed?
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * CRStopDrawing
 | 
						|
 *  When direct access to a window's buffer is no longer needed, this
 | 
						|
 *  routine should be called to allow CoreGraphics to compress or
 | 
						|
 *  move it.
 | 
						|
 */
 | 
						|
static void
 | 
						|
CRStopDrawing(RootlessFrameID wid, Bool flush)
 | 
						|
{
 | 
						|
    CRWindowPtr crWinPtr = (CRWindowPtr) wid;
 | 
						|
 | 
						|
    UnlockPortBits(crWinPtr->port);
 | 
						|
 | 
						|
    if (flush) {
 | 
						|
        QDFlushPortBuffer(crWinPtr->port, NULL);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * CRUpdateRegion
 | 
						|
 *  Flush a region from a window's backing buffer to the screen.
 | 
						|
 */
 | 
						|
static void
 | 
						|
CRUpdateRegion(RootlessFrameID wid, RegionPtr pDamage)
 | 
						|
{
 | 
						|
    CRWindowPtr crWinPtr = (CRWindowPtr) wid;
 | 
						|
 | 
						|
#ifdef ROOTLESS_TRACK_DAMAGE
 | 
						|
    int count = REGION_NUM_RECTS(pDamage);
 | 
						|
    BoxRec *rects = REGION_RECTS(pDamage);
 | 
						|
    BoxRec *end;
 | 
						|
 | 
						|
    static RgnHandle rgn = NULL;
 | 
						|
    static RgnHandle box = NULL;
 | 
						|
 | 
						|
    if (!rgn) rgn = NewRgn();
 | 
						|
    if (!box) box = NewRgn();
 | 
						|
 | 
						|
    for (end = rects+count; rects < end; rects++) {
 | 
						|
        Rect qdRect;
 | 
						|
        qdRect.left = rects->x1;
 | 
						|
        qdRect.top = rects->y1;
 | 
						|
        qdRect.right = rects->x2;
 | 
						|
        qdRect.bottom = rects->y2;
 | 
						|
 | 
						|
        RectRgn(box, &qdRect);
 | 
						|
        UnionRgn(rgn, box, rgn);
 | 
						|
    }
 | 
						|
 | 
						|
    QDFlushPortBuffer(crWinPtr->port, rgn);
 | 
						|
 | 
						|
    SetEmptyRgn(rgn);
 | 
						|
    SetEmptyRgn(box);
 | 
						|
 | 
						|
#else	/* !ROOTLESS_TRACK_DAMAGE */
 | 
						|
    QDFlushPortBuffer(crWinPtr->port, NULL);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * CRDamageRects
 | 
						|
 *  Mark damaged rectangles as requiring redisplay to screen.
 | 
						|
 */
 | 
						|
static void
 | 
						|
CRDamageRects(RootlessFrameID wid, int count, const BoxRec *rects,
 | 
						|
               int shift_x, int shift_y)
 | 
						|
{
 | 
						|
    CRWindowPtr crWinPtr = (CRWindowPtr) wid;
 | 
						|
    const BoxRec *end;
 | 
						|
 | 
						|
    for (end = rects + count; rects < end; rects++) {
 | 
						|
        Rect qdRect;
 | 
						|
        qdRect.left = rects->x1 + shift_x;
 | 
						|
        qdRect.top = rects->y1 + shift_y;
 | 
						|
        qdRect.right = rects->x2 + shift_x;
 | 
						|
        qdRect.bottom = rects->y2 + shift_y;
 | 
						|
 | 
						|
        QDAddRectToDirtyRegion(crWinPtr->port, &qdRect);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * Called to check if the frame should be reordered when it is restacked.
 | 
						|
 */
 | 
						|
Bool CRDoReorderWindow(RootlessWindowPtr pFrame)
 | 
						|
{
 | 
						|
    WindowPtr pWin = pFrame->win;
 | 
						|
 | 
						|
    return AppleWMDoReorderWindow(pWin);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static RootlessFrameProcsRec CRRootlessProcs = {
 | 
						|
    CRCreateFrame,
 | 
						|
    CRDestroyFrame,
 | 
						|
    CRMoveFrame,
 | 
						|
    CRResizeFrame,
 | 
						|
    CRRestackFrame,
 | 
						|
    CRReshapeFrame,
 | 
						|
    CRUnmapFrame,
 | 
						|
    CRStartDrawing,
 | 
						|
    CRStopDrawing,
 | 
						|
    CRUpdateRegion,
 | 
						|
    CRDamageRects,
 | 
						|
    NULL,
 | 
						|
    CRDoReorderWindow,
 | 
						|
    NULL,
 | 
						|
    NULL,
 | 
						|
    NULL,
 | 
						|
    NULL
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * Initialize CR implementation
 | 
						|
 */
 | 
						|
Bool
 | 
						|
CRInit(ScreenPtr pScreen)
 | 
						|
{
 | 
						|
    RootlessInit(pScreen, &CRRootlessProcs);
 | 
						|
 | 
						|
    rootless_CopyBytes_threshold = 0;
 | 
						|
    rootless_FillBytes_threshold = 0;
 | 
						|
    rootless_CompositePixels_threshold = 0;
 | 
						|
    rootless_CopyWindow_threshold = 0;
 | 
						|
 | 
						|
    return TRUE;
 | 
						|
}
 |