/* $XdotOrg: xc/programs/Xserver/hw/darwin/quartz_1.3/rootlessAquaImp.m,v 1.1.4.1.4.2 2004/03/04 17:47:31 eich Exp $ */
/*
 * Rootless implementation for Mac OS X Aqua environment
 */
/* $XFree86: xc/programs/Xserver/hw/darwin/bundle/rootlessAquaImp.m,v 1.11 2002/01/17 02:44:27 torrey Exp $ */

#include "rootlessAquaImp.h"
#include "XWindow.h"
#include "fakeBoxRec.h"
#include "quartzCommon.h"
#include "pseudoramiX.h"

extern void ErrorF(const char *, ...);

typedef struct {
    XWindow *window;
} AquaWindowRec;


#define WINREC(rw) ((AquaWindowRec *)rw)


// Multihead note: When rootless mode uses PseudoramiX, the
// X server only sees one screen; only PseudoramiX itself knows
// about all of the screens.

int AquaDisplayCount()
{
    aquaNumScreens = [[NSScreen screens] count];

    if (noPseudoramiXExtension) {
        return aquaNumScreens;
    } else {
        return 1; // only PseudoramiX knows about the rest
    }
}

void AquaScreenInit(int index, int *x, int *y, int *width, int *height,
                    int *rowBytes, unsigned long *bps, unsigned long *spp,
                    int *bpp)
{
    *bps = 8;
    *spp = 3;
    *bpp = 32;

    if (noPseudoramiXExtension) {
        NSScreen *screen = [[NSScreen screens] objectAtIndex:index];
        NSRect frame = [screen frame];

        // set x, y so (0,0) is top left of main screen
        *x = NSMinX(frame);
        *y = NSHeight([[NSScreen mainScreen] frame]) - NSHeight(frame) -
            NSMinY(frame);

        *width =  NSWidth(frame);
        *height = NSHeight(frame);
        *rowBytes = (*width) * (*bpp) / 8;

        // Shift the usable part of main screen down to avoid the menu bar.
        if (NSEqualRects(frame, [[NSScreen mainScreen] frame])) {
            *y      += aquaMenuBarHeight;
            *height -= aquaMenuBarHeight;
        }

    } else {
        int i;
        NSRect unionRect = NSMakeRect(0, 0, 0, 0);
        NSArray *screens = [NSScreen screens];

        // Get the union of all screens (minus the menu bar on main screen)
        for (i = 0; i < [screens count]; i++) {
            NSScreen *screen = [screens objectAtIndex:i];
            NSRect frame = [screen frame];
            frame.origin.y = [[NSScreen mainScreen] frame].size.height -
                             frame.size.height - frame.origin.y;
            if (NSEqualRects([screen frame], [[NSScreen mainScreen] frame])) {
                frame.origin.y    += aquaMenuBarHeight;
                frame.size.height -= aquaMenuBarHeight;
            }
            unionRect = NSUnionRect(unionRect, frame);
        }

        // Use unionRect as the screen size for the X server.
        *x = unionRect.origin.x;
        *y = unionRect.origin.y;
        *width = unionRect.size.width;
        *height = unionRect.size.height;
        *rowBytes = (*width) * (*bpp) / 8;

        // Tell PseudoramiX about the real screens.
        // InitOutput() will move the big screen to (0,0),
        // so compensate for that here.
        for (i = 0; i < [screens count]; i++) {
            NSScreen *screen = [screens objectAtIndex:i];
            NSRect frame = [screen frame];
            int j;

            // Skip this screen if it's a mirrored copy of an earlier screen.
            for (j = 0; j < i; j++) {
                if (NSEqualRects(frame, [[screens objectAtIndex:j] frame])) {
                    ErrorF("PseudoramiX screen %d is a mirror of screen %d.\n",
                           i, j);
                    break;
                }
            }
            if (j < i) continue; // this screen is a mirrored copy

            frame.origin.y = [[NSScreen mainScreen] frame].size.height -
                             frame.size.height - frame.origin.y;

            if (NSEqualRects([screen frame], [[NSScreen mainScreen] frame])) {
                frame.origin.y    += aquaMenuBarHeight;
                frame.size.height -= aquaMenuBarHeight;
            }

            ErrorF("PseudoramiX screen %d added: %dx%d @ (%d,%d).\n", i,
                   (int)frame.size.width, (int)frame.size.height,
                   (int)frame.origin.x, (int)frame.origin.y);

            frame.origin.x -= unionRect.origin.x;
            frame.origin.y -= unionRect.origin.y;

            ErrorF("PseudoramiX screen %d placed at X11 coordinate (%d,%d).\n",
                   i, (int)frame.origin.x, (int)frame.origin.y);

            PseudoramiXAddScreen(frame.origin.x, frame.origin.y,
                                 frame.size.width, frame.size.height);
        }
    }
}

void *AquaNewWindow(void *upperw, int x, int y, int w, int h, int isRoot)
{
    AquaWindowRec *winRec = (AquaWindowRec *)malloc(sizeof(AquaWindowRec));
    NSRect frame = NSMakeRect(x, NSHeight([[NSScreen mainScreen] frame]) -
                              y - h, w, h);

    winRec->window = [[XWindow alloc] initWithContentRect:frame isRoot:isRoot];

    if (upperw) {
        AquaWindowRec *upperRec = WINREC(upperw);
        int uppernum = [upperRec->window windowNumber];
        [winRec->window orderWindow:NSWindowBelow relativeTo:uppernum];
    } else {
        [winRec->window orderFront:nil];
    }

    // fixme hide root for now
    if (isRoot) [winRec->window orderOut:nil];

    return winRec;
}

void AquaDestroyWindow(void *rw)
{
    AquaWindowRec *winRec = WINREC(rw);

    [winRec->window release];
}

void AquaMoveWindow(void *rw, int x, int y)
{
    AquaWindowRec *winRec = WINREC(rw);
    NSPoint topLeft = NSMakePoint(x, NSHeight([[NSScreen mainScreen] frame]) -
                                  y);

    [winRec->window setFrameTopLeftPoint:topLeft];
}

void AquaStartResizeWindow(void *rw, int x, int y, int w, int h)
{
    AquaWindowRec *winRec = WINREC(rw);
    NSRect frame = NSMakeRect(x, NSHeight([[NSScreen mainScreen] frame]) -
                              y - h, w, h);

    [winRec->window setFrame:frame display:NO];
}

void AquaFinishResizeWindow(void *rw, int x, int y, int w, int h)
{
    // refresh everything? fixme yes for testing
    fakeBoxRec box = {0, 0, w, h};
    AquaWindowRec *winRec = WINREC(rw);

    [winRec->window refreshRects:&box count:1];
}

void AquaUpdateRects(void *rw, fakeBoxRec *rects, int count)
{
    AquaWindowRec *winRec = WINREC(rw);

    [winRec->window refreshRects:rects count:count];
}

// fixme is this upperw or lowerw?
void AquaRestackWindow(void *rw, void *upperw)
{
    AquaWindowRec *winRec = WINREC(rw);

    if (upperw) {
        AquaWindowRec *upperRec = WINREC(upperw);
        int uppernum = [upperRec->window windowNumber];
        [winRec->window orderWindow:NSWindowBelow relativeTo:uppernum];
    } else {
        [winRec->window orderFront:nil];
    }
    // [winRec->window setAcceptsMouseMovedEvents:YES];
    // fixme prefer to orderFront whenever possible - pass upperw, not lowerw
}

// rects are the areas not part of the new shape
void AquaReshapeWindow(void *rw, fakeBoxRec *rects, int count)
{
    AquaWindowRec *winRec = WINREC(rw);

    // make transparent if window is now shaped
    // transparent windows never go back to opaque
    if (count > 0) {
        [winRec->window setOpaque:NO];
    }

    [[winRec->window contentView] reshapeRects:rects count:count];

    if (! [winRec->window isOpaque]) {
        // force update of window shadow
        [winRec->window setHasShadow:NO];
        [winRec->window setHasShadow:YES];
    }
}

void AquaGetPixmap(void *rw, char **bits,
                   int *rowBytes, int *depth, int *bpp)
{
    AquaWindowRec *winRec = WINREC(rw);

    [winRec->window getBits:bits rowBytes:rowBytes depth:depth
                    bitsPerPixel:bpp];
}
