--- /dev/null
+/**************************************************************
+ *
+ * Quartz-specific support for the Darwin X Server
+ *
+ **************************************************************/
+/*
+ * Copyright (c) 2001 Greg Parker and Torrey T. Lyons.
+ * Copyright (c) 2002 Apple Computer, Inc.
+ * 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.
+ */
+/* $XFree86: xc/programs/Xserver/hw/darwin/quartz/quartz.c,v 1.1 2002/03/28 02:21:18 torrey Exp $ */
+
+#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>
+
+#include <AvailabilityMacros.h>
+#include <CoreGraphics/CoreGraphics.h>
+
+/* Shared global variables for Quartz modes */
+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)
+ {
+ /* Remove Aqua menubar from display bounds. */
+
+ 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; /* FIXME: hardcoded maximum */
+ CGRect unionRect = CGRectNull, frame;
+ CGDirectDisplayID screens[total];
+
+ CGGetActiveDisplayList (total, screens, &total);
+
+ /* Get the union of all screens */
+ for (i = 0; i < total; i++)
+ {
+ CGDirectDisplayID dpy = screens[i];
+
+ /* we can't remove the menubar from the screen - doing so
+ would constrain the pointer to the screen, not allowing it
+ to reach the menubar.. */
+
+ frame = display_screen_bounds (dpy, FALSE);
+ unionRect = CGRectUnion (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;
+
+ /* Tell PseudoramiX about the real screens. */
+ 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);
+ }
+}
+
+/* Do mode dependent initialization of each screen for Quartz. */
+Bool
+QuartzAddScreen (int index, ScreenPtr pScreen)
+{
+ DarwinFramebufferPtr dfb = SCREEN_PRIV(pScreen);
+
+ /* If no specific depth chosen, look for the depth of the main display.
+ Else if 16bpp specified, use that. Else use 32bpp. */
+
+ 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;
+
+ /* Passing zero width (pitch) makes miCreateScreenResources set the
+ screen pixmap to the framebuffer pointer, i.e. null. We'll take
+ it from there.. */
+ dfb->pitch = 0;
+ dfb->framebuffer = NULL;
+
+ DRIScreenInit (pScreen);
+
+ return TRUE;
+}
+
+/* Finalize mode specific setup of each screen. */
+Bool
+QuartzSetupScreen (int index, ScreenPtr pScreen)
+{
+ // do full screen or rootless specific setup
+ if (! RootlessSetupScreen(index, pScreen))
+ return FALSE;
+
+ // setup cursor support
+ if (! QuartzInitCursor(pScreen))
+ return FALSE;
+
+ DRIFinishScreenInit (pScreen);
+
+ return TRUE;
+}
+
+
+/* Quartz display initialization. */
+void
+QuartzInitOutput (int argc, char **argv)
+{
+ static int orig_noPanoramiXExtension;
+ int total;
+
+ if (serverGeneration == 1) {
+ orig_noPanoramiXExtension = noPanoramiXExtension;
+ QuartzAudioInit();
+ }
+
+ /* +xinerama option sets noPanoramiXExtension variable */
+ noPseudoramiXExtension = orig_noPanoramiXExtension;
+
+ total = 16; /* FIXME: hardcoded maximum */
+ if (total > 0)
+ {
+ CGDirectDisplayID screens[total];
+ CGGetActiveDisplayList (total, screens, &total);
+ }
+
+ if (noPseudoramiXExtension)
+ darwinScreensFound = total;
+ else
+ darwinScreensFound = 1; // only PseudoramiX knows about the rest
+
+ if (!quartzEnableRootless)
+ RootlessHideAllWindows ();
+}
+
+/* This function from randr.c */
+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)
+ {
+ /* FIXME: if not using Xinerama, we have multiple screens, and
+ to do this properly may need to add or remove screens. Which
+ isn't possible. So don't do anything. Another reason why
+ we default to running with Xinerama. */
+
+ 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;
+
+ /* FIXME: should probably do something with RandR here. */
+
+ DarwinAdjustScreenOrigins (&screenInfo);
+ RootlessRepositionWindows (screenInfo.screens[0]);
+ RootlessUpdateScreenPixmap (screenInfo.screens[0]);
+
+ sx = dixScreenOrigins[pScreen->myNum].x + darwinMainScreenX;
+ sy = dixScreenOrigins[pScreen->myNum].y + darwinMainScreenY;
+
+ /* Adjust the root window. */
+
+ pRoot = WindowTable[pScreen->myNum];
+ pScreen->ResizeWindow (pRoot, x - sx, y - sy, width, height, NULL);
+ pScreen->PaintWindowBackground (pRoot, &pRoot->borderClip, PW_BACKGROUND);
+ QuartzIgnoreNextWarpCursor ();
+ DefineInitialRootWindow (pRoot);
+
+ /* Send an event for the root reconfigure */
+
+ 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);
+
+ /* FIXME: what does this do? */
+ RREditConnectionInfo (pScreen);
+}
+
+static void
+do_exec (void (*callback) (void *data), void *data)
+{
+ /* Do the fork-twice trick to avoid needing to reap zombies */
+
+ int child1, child2 = 0;
+ int status;
+
+ /* we should really try to report errors here.. */
+
+ child1 = fork ();
+
+ switch (child1)
+ {
+ case -1: /* error */
+ break;
+
+ case 0: /* child1 */
+ child2 = fork ();
+
+ switch (child2)
+ {
+ int max_files, i;
+ char buf[1024], *tem;
+
+ case -1: /* error */
+ _exit (1);
+
+ case 0: /* child2 */
+ /* close all open files except for standard streams */
+ max_files = sysconf (_SC_OPEN_MAX);
+ for (i = 3; i < max_files; i++)
+ close (i);
+
+ /* ensure stdin is on /dev/null */
+ close (0);
+ open ("/dev/null", O_RDONLY);
+
+ /* cd $HOME */
+ tem = getenv ("HOME");
+ if (tem != NULL)
+ chdir (tem);
+
+ /* Setup environment */
+ snprintf (buf, sizeof (buf), ":%s", display);
+ setenv ("DISPLAY", buf, TRUE);
+ tem = getenv ("PATH");
+ if (tem != NULL && tem[0] != NULL)
+ snprintf (buf, sizeof (buf), "%s:/usr/X11R6/bin", tem);
+ else
+ snprintf (buf, sizeof (buf), "/bin:/usr/bin:/usr/X11R6/bin");
+ setenv ("PATH", buf, TRUE);
+
+ (*callback) (data);
+
+ _exit (2);
+
+ default: /* parent (child1) */
+ _exit (0);
+ }
+ break;
+
+ default: /* parent */
+ waitpid (child1, &status, 0);
+ }
+}
+
+static void
+run_client_callback (void *data)
+{
+ char **argv = data;
+ execvp (argv[0], argv);
+}
+
+/* Note that this function is called from both X server and appkit threads */
+void
+QuartzRunClient (const char *command)
+{
+ const char *shell;
+ const char *argv[5];
+
+ shell = getenv ("SHELL");
+ if (shell == NULL)
+ shell = "/bin/bash";
+
+ /* At least [ba]sh, [t]csh and zsh all work with this syntax. We
+ need to use an interactive shell to force it to load the user's
+ environment. Otherwise things like fink don't work at all well.. */
+
+ argv[0] = shell;
+ argv[1] = "-i";
+ argv[2] = "-c";
+ argv[3] = command;
+ argv[4] = NULL;
+
+ do_exec (run_client_callback, argv);
+}
+
+static void
+QuartzSetFullscreen (Bool state)
+{
+ if (quartzHasRoot == state)
+ return;
+
+ quartzHasRoot = state;
+
+ xp_disable_update ();
+
+ if (!quartzHasRoot && !quartzEnableRootless)
+ RootlessHideAllWindows ();
+
+ RootlessUpdateRooted (quartzHasRoot);
+
+ if (quartzHasRoot && !quartzEnableRootless)
+ RootlessShowAllWindows ();
+
+ /* Only update screen info when something is visible. Avoids the wm
+ moving the windows out from under the menubar when it shouldn't */
+
+ if (quartzHasRoot || quartzEnableRootless)
+ QuartzUpdateScreens ();
+
+ /* Somehow the menubar manages to interfere with our event stream
+ in fullscreen mode, even though it's not visible. */
+
+ 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 ();
+ }
+}
+
+/* Show the X server on screen. Does nothing if already shown. Restore the
+ X clip regions the X server cursor state. */
+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]);
+ }
+
+ /* FIXME: not sure about this, it may need to have a preference like
+ in XDarwin..? */
+
+ if (!quartzEnableRootless)
+ QuartzSetFullscreen (TRUE);
+}
+
+/* Remove the X server display from the screen. Does nothing if already
+ hidden. Set X clip regions to prevent drawing, and restore the Aqua
+ cursor. */
+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;
+}
+
+/* Cleanup before X server shutdown. Release the screen and restore the
+ Aqua cursor. */
+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 )
+{
+ /* This arg is passed when launched from the Aqua GUI. */
+ 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 ();
+ }
+}