/************************************************************** * * 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 #include #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 /* 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] != 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: /* 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, int needs_quoting) { const char *shell; const char *argv[5]; char *quoted = NULL; 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; if (needs_quoting) { /* FIXME: do better 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 (); /* 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 (); } }