X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=synfig-osx%2Ftrunk%2Flauncher%2Fquartz.c;fp=synfig-osx%2Ftrunk%2Flauncher%2Fquartz.c;h=0e9ae7c80be1fda8810d83d0ea81aa21906b221e;hb=4f638c4f8e5dc642a92d62da31233654eca397b2;hp=0000000000000000000000000000000000000000;hpb=7bf63b38a939d592d8a7c0c1f52aadfda2229427;p=synfig.git diff --git a/synfig-osx/trunk/launcher/quartz.c b/synfig-osx/trunk/launcher/quartz.c new file mode 100644 index 0000000..0e9ae7c --- /dev/null +++ b/synfig-osx/trunk/launcher/quartz.c @@ -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 +#include +#include +#include +#include + +#include +#include + +/* 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 (); + } +}