#include <AvailabilityMacros.h>
#include <CoreGraphics/CoreGraphics.h>
#include "quartz.h"
#include "darwin.h"
#include "quartz-audio.h"
#include "quartz-cursor.h"
#include "rootless.h"
#include "rootless-window.h"
#include "pseudoramiX.h"
#include "globals.h"
#include "dri.h"
#define _APPLEWM_SERVER_
#include "applewmstr.h"
#include "X11Application.h"
#include "scrnintstr.h"
#include "colormapst.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
int quartzEventWriteFD = -1;
int quartzUseSysBeep = 1;
int quartzServerVisible = FALSE;
int quartzDesiredDepth = -1;
int quartzHasRoot = FALSE, quartzEnableRootless = TRUE;
int quartzFullscreenDisableHotkeys = TRUE;
int noPseudoramiXExtension = FALSE;
int quartzXpluginOptions = 0;
extern char *display;
static CGDirectDisplayID
display_at_index (int index)
{
CGError err;
CGDisplayCount cnt;
CGDirectDisplayID dpy[index+1];
err = CGGetActiveDisplayList (index + 1, dpy, &cnt);
if (err == kCGErrorSuccess && (int) cnt == index + 1)
return dpy[index];
else
return kCGNullDirectDisplay;
}
static CGRect
display_screen_bounds (CGDirectDisplayID id, Bool remove_menubar)
{
CGRect frame;
frame = CGDisplayBounds (id);
if (remove_menubar && !quartzHasRoot
&& frame.origin.x == 0 && frame.origin.y == 0)
{
frame.origin.y += 22;
frame.size.height -= 22;
}
return frame;
}
static void
addPseudoramiXScreens (int *x, int *y, int *width, int *height)
{
CGDisplayCount i, total = 16;
CGRect unionRect = CGRectNull, frame;
CGDirectDisplayID screens[total];
CGGetActiveDisplayList (total, screens, &total);
for (i = 0; i < total; i++)
{
CGDirectDisplayID dpy = screens[i];
frame = display_screen_bounds (dpy, FALSE);
unionRect = CGRectUnion (unionRect, frame);
}
*x = unionRect.origin.x;
*y = unionRect.origin.y;
*width = unionRect.size.width;
*height = unionRect.size.height;
for (i = 0; i < total; i++)
{
CGDirectDisplayID dpy = screens[i];
frame = display_screen_bounds (dpy, TRUE);
#ifdef DEBUG
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);
#endif
frame.origin.x -= unionRect.origin.x;
frame.origin.y -= unionRect.origin.y;
#ifdef DEBUG
ErrorF("PseudoramiX screen %d placed at X11 coordinate (%d,%d).\n",
i, (int)frame.origin.x, (int)frame.origin.y);
#endif
PseudoramiXAddScreen(frame.origin.x, frame.origin.y,
frame.size.width, frame.size.height);
}
}
Bool
QuartzAddScreen (int index, ScreenPtr pScreen)
{
DarwinFramebufferPtr dfb = SCREEN_PRIV(pScreen);
dfb->componentCount = 3;
dfb->bitsPerComponent = 8;
dfb->bitsPerPixel = 32;
if (quartzDesiredDepth == -1)
{
dfb->bitsPerComponent = CGDisplayBitsPerSample (kCGDirectMainDisplay);
dfb->bitsPerPixel = CGDisplayBitsPerPixel (kCGDirectMainDisplay);
}
else if (quartzDesiredDepth == 15)
{
dfb->bitsPerComponent = 5;
dfb->bitsPerPixel = 16;
}
else if (quartzDesiredDepth == 8)
{
dfb->bitsPerComponent = 8;
dfb->bitsPerPixel = 8;
dfb->componentCount = 1;
}
if (noPseudoramiXExtension)
{
CGDirectDisplayID dpy;
CGRect frame;
dpy = display_at_index (index);
frame = display_screen_bounds (dpy, TRUE);
dfb->x = frame.origin.x;
dfb->y = frame.origin.y;
dfb->width = frame.size.width;
dfb->height = frame.size.height;
}
else
{
addPseudoramiXScreens (&dfb->x, &dfb->y, &dfb->width, &dfb->height);
}
dfb->colorBitsPerPixel = dfb->bitsPerComponent * dfb->componentCount;
dfb->pitch = 0;
dfb->framebuffer = NULL;
DRIScreenInit (pScreen);
return TRUE;
}
Bool
QuartzSetupScreen (int index, ScreenPtr pScreen)
{
if (! RootlessSetupScreen(index, pScreen))
return FALSE;
if (! QuartzInitCursor(pScreen))
return FALSE;
DRIFinishScreenInit (pScreen);
return TRUE;
}
void
QuartzInitOutput (int argc, char **argv)
{
static int orig_noPanoramiXExtension;
int total;
if (serverGeneration == 1) {
orig_noPanoramiXExtension = noPanoramiXExtension;
QuartzAudioInit();
}
noPseudoramiXExtension = orig_noPanoramiXExtension;
total = 16;
if (total > 0)
{
CGDirectDisplayID screens[total];
CGGetActiveDisplayList (total, screens, &total);
}
if (noPseudoramiXExtension)
darwinScreensFound = total;
else
darwinScreensFound = 1;
if (!quartzEnableRootless)
RootlessHideAllWindows ();
}
extern char *ConnectionInfo;
static int padlength[4] = {0, 3, 2, 1};
static void
RREditConnectionInfo (ScreenPtr pScreen)
{
xConnSetup *connSetup;
char *vendor;
xPixmapFormat *formats;
xWindowRoot *root;
xDepth *depth;
xVisualType *visual;
int screen = 0;
int d;
connSetup = (xConnSetup *) ConnectionInfo;
vendor = (char *) connSetup + sizeof (xConnSetup);
formats = (xPixmapFormat *) ((char *) vendor +
connSetup->nbytesVendor +
padlength[connSetup->nbytesVendor & 3]);
root = (xWindowRoot *) ((char *) formats +
sizeof (xPixmapFormat) * screenInfo.numPixmapFormats);
while (screen != pScreen->myNum)
{
depth = (xDepth *) ((char *) root +
sizeof (xWindowRoot));
for (d = 0; d < root->nDepths; d++)
{
visual = (xVisualType *) ((char *) depth +
sizeof (xDepth));
depth = (xDepth *) ((char *) visual +
depth->nVisuals * sizeof (xVisualType));
}
root = (xWindowRoot *) ((char *) depth);
screen++;
}
root->pixWidth = pScreen->width;
root->pixHeight = pScreen->height;
root->mmWidth = pScreen->mmWidth;
root->mmHeight = pScreen->mmHeight;
}
static void
QuartzUpdateScreens (void)
{
ScreenPtr pScreen;
WindowPtr pRoot;
int x, y, width, height, sx, sy;
xEvent e;
if (noPseudoramiXExtension || screenInfo.numScreens != 1)
{
return;
}
pScreen = screenInfo.screens[0];
PseudoramiXResetScreens ();
addPseudoramiXScreens (&x, &y, &width, &height);
dixScreenOrigins[pScreen->myNum].x = x;
dixScreenOrigins[pScreen->myNum].y = y;
pScreen->mmWidth = pScreen->mmWidth * ((double) width / pScreen->width);
pScreen->mmHeight = pScreen->mmHeight * ((double) height / pScreen->height);
pScreen->width = width;
pScreen->height = height;
DarwinAdjustScreenOrigins (&screenInfo);
RootlessRepositionWindows (screenInfo.screens[0]);
RootlessUpdateScreenPixmap (screenInfo.screens[0]);
sx = dixScreenOrigins[pScreen->myNum].x + darwinMainScreenX;
sy = dixScreenOrigins[pScreen->myNum].y + darwinMainScreenY;
pRoot = WindowTable[pScreen->myNum];
pScreen->ResizeWindow (pRoot, x - sx, y - sy, width, height, NULL);
pScreen->PaintWindowBackground (pRoot, &pRoot->borderClip, PW_BACKGROUND);
QuartzIgnoreNextWarpCursor ();
DefineInitialRootWindow (pRoot);
e.u.u.type = ConfigureNotify;
e.u.configureNotify.window = pRoot->drawable.id;
e.u.configureNotify.aboveSibling = None;
e.u.configureNotify.x = x - sx;
e.u.configureNotify.y = y - sy;
e.u.configureNotify.width = width;
e.u.configureNotify.height = height;
e.u.configureNotify.borderWidth = wBorderWidth (pRoot);
e.u.configureNotify.override = pRoot->overrideRedirect;
DeliverEvents (pRoot, &e, 1, NullWindow);
RREditConnectionInfo (pScreen);
}
static void
do_exec (void (*callback) (void *data), void *data)
{
int child1, child2 = 0;
int status;
child1 = fork ();
switch (child1)
{
case -1:
break;
case 0:
child2 = fork ();
switch (child2)
{
int max_files, i;
char buf[1024], *tem;
case -1:
_exit (1);
case 0:
max_files = sysconf (_SC_OPEN_MAX);
for (i = 3; i < max_files; i++)
close (i);
close (0);
open ("/dev/null", O_RDONLY);
tem = getenv ("HOME");
if (tem != NULL)
chdir (tem);
snprintf (buf, sizeof (buf), ":%s", display);
setenv ("DISPLAY", buf, TRUE);
tem = getenv ("PATH");
if (tem != NULL && tem[0] != 0)
snprintf (buf, sizeof (buf), "%s:/usr/X11R6/bin", tem);
else
snprintf (buf, sizeof (buf), "/bin:/usr/bin:/usr/X11R6/bin");
setenv ("PATH", buf, TRUE);
tem = getenv ("MANPATH");
if (tem != NULL && tem[0] != 0)
snprintf (buf, sizeof (buf), "%s:/usr/X11R6/man", tem);
else
snprintf (buf, sizeof (buf), "/usr/man:/usr/X11R6/man");
setenv ("MANPATH", buf, TRUE);
(*callback) (data);
_exit (2);
default:
_exit (0);
}
break;
default:
waitpid (child1, &status, 0);
}
}
static void
run_client_callback (void *data)
{
char **argv = data;
execvp (argv[0], argv);
}
void
QuartzRunClient (const char *command, int needs_quoting)
{
const char *shell;
const char *argv[5];
char *quoted = NULL;
shell = getenv ("SHELL");
if (shell == NULL)
shell = "/bin/bash";
argv[0] = shell;
argv[1] = "-i";
argv[2] = "-c";
argv[3] = command;
argv[4] = NULL;
if (needs_quoting)
{
quoted = malloc (strlen (command) + 3);
sprintf (quoted, "'%s'", command);
argv[3] = quoted;
}
do_exec (run_client_callback, argv);
if (quoted != NULL)
free (quoted);
}
static void
QuartzSetFullscreen (Bool state)
{
if (quartzHasRoot == state)
return;
quartzHasRoot = state;
xp_disable_update ();
if (!quartzHasRoot && !quartzEnableRootless)
RootlessHideAllWindows ();
RootlessUpdateRooted (quartzHasRoot);
if (quartzHasRoot && !quartzEnableRootless)
RootlessShowAllWindows ();
if (quartzHasRoot || quartzEnableRootless)
QuartzUpdateScreens ();
X11ApplicationShowHideMenubar (!quartzHasRoot);
xp_reenable_update ();
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
if (quartzFullscreenDisableHotkeys)
xp_disable_hot_keys (quartzHasRoot);
#endif
}
static void
QuartzSetRootless (Bool state)
{
if (quartzEnableRootless == state)
return;
quartzEnableRootless = state;
if (!quartzEnableRootless && !quartzHasRoot)
{
xp_disable_update ();
RootlessHideAllWindows ();
xp_reenable_update ();
}
else if (quartzEnableRootless && !quartzHasRoot)
{
xp_disable_update ();
RootlessShowAllWindows ();
QuartzUpdateScreens ();
xp_reenable_update ();
}
}
static void
QuartzShow (void)
{
int i;
if (quartzServerVisible)
return;
quartzServerVisible = TRUE;
for (i = 0; i < screenInfo.numScreens; i++)
{
if (screenInfo.screens[i])
QuartzResumeXCursor(screenInfo.screens[i]);
}
if (!quartzEnableRootless)
QuartzSetFullscreen (TRUE);
}
static void
QuartzHide (void)
{
int i;
if (!quartzServerVisible)
return;
for (i = 0; i < screenInfo.numScreens; i++)
{
if (screenInfo.screens[i])
QuartzSuspendXCursor(screenInfo.screens[i]);
}
QuartzSetFullscreen (FALSE);
quartzServerVisible = FALSE;
}
void
QuartzGiveUp (void)
{
int i;
for (i = 0; i < screenInfo.numScreens; i++) {
if (screenInfo.screens[i]) {
QuartzSuspendXCursor(screenInfo.screens[i]);
}
}
}
int
QuartzProcessArgument( int argc, char *argv[], int i )
{
if (strncmp (argv[i], "-psn_", 5) == 0)
{
return 1;
}
if (strcmp (argv[i], "-depth") == 0)
{
int arg;
if (i == argc - 1)
FatalError ("-depth requires an argument\n");
arg = atoi (argv[i + 1]);
if (arg == 8 || arg == 15 || arg == 24)
quartzDesiredDepth = arg;
else
FatalError ("Only 8, 15 and 24 bit color depths are supported.\n");
return 2;
}
return 0;
}
void
QuartzClientMessage (const xEvent *xe)
{
switch (xe->u.clientMessage.u.l.type)
{
case kXquartzControllerNotify:
AppleWMSendEvent (AppleWMControllerNotify,
AppleWMControllerNotifyMask,
xe->u.clientMessage.u.l.longs0,
xe->u.clientMessage.u.l.longs1);
break;
case kXquartzPasteboardNotify:
AppleWMSendEvent (AppleWMPasteboardNotify,
AppleWMPasteboardNotifyMask,
xe->u.clientMessage.u.l.longs0,
xe->u.clientMessage.u.l.longs1);
break;
case kXquartzActivate:
QuartzShow ();
AppleWMSendEvent (AppleWMActivationNotify,
AppleWMActivationNotifyMask,
AppleWMIsActive, 0);
break;
case kXquartzDeactivate:
AppleWMSendEvent (AppleWMActivationNotify,
AppleWMActivationNotifyMask,
AppleWMIsInactive, 0);
QuartzHide ();
break;
case kXquartzDisplayChanged:
QuartzUpdateScreens ();
break;
case kXquartzWindowState:
RootlessNativeWindowStateChanged (xe->u.clientMessage.u.l.longs0,
xe->u.clientMessage.u.l.longs1);
break;
case kXquartzWindowMoved:
RootlessNativeWindowMoved (xe->u.clientMessage.u.l.longs0);
break;
case kXquartzToggleFullscreen:
if (quartzEnableRootless)
QuartzSetFullscreen (!quartzHasRoot);
else if (quartzHasRoot)
QuartzHide ();
else
QuartzShow ();
break;
case kXquartzSetRootless:
QuartzSetRootless (xe->u.clientMessage.u.l.longs0);
if (!quartzEnableRootless && !quartzHasRoot)
QuartzHide ();
}
}