Remove ancient trunk folder from svn repository
[synfig.git] / synfig-osx / launcher / quartz.c
diff --git a/synfig-osx/launcher/quartz.c b/synfig-osx/launcher/quartz.c
new file mode 100644 (file)
index 0000000..0e9ae7c
--- /dev/null
@@ -0,0 +1,683 @@
+/**************************************************************
+ *
+ * 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 ();
+    }
+}