1 /**************************************************************
3 * Quartz-specific support for the Darwin X Server
5 **************************************************************/
7 * Copyright (c) 2001 Greg Parker and Torrey T. Lyons.
8 * Copyright (c) 2002 Apple Computer, Inc.
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
18 * The above copyright notice and this permission notice shall be included in
19 * all copies or substantial portions of the Software.
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
25 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
26 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 * DEALINGS IN THE SOFTWARE.
29 * Except as contained in this notice, the name(s) of the above copyright
30 * holders shall not be used in advertising or otherwise to promote the sale,
31 * use or other dealings in this Software without prior written authorization.
33 /* $XFree86: xc/programs/Xserver/hw/darwin/quartz/quartz.c,v 1.1 2002/03/28 02:21:18 torrey Exp $ */
37 #include "quartz-audio.h"
38 #include "quartz-cursor.h"
40 #include "rootless-window.h"
41 #include "pseudoramiX.h"
44 #define _APPLEWM_SERVER_
45 #include "applewmstr.h"
46 #include "X11Application.h"
48 #include "scrnintstr.h"
49 #include "colormapst.h"
51 #include <sys/types.h>
57 #include <AvailabilityMacros.h>
58 #include <CoreGraphics/CoreGraphics.h>
60 /* Shared global variables for Quartz modes */
61 int quartzEventWriteFD = -1;
62 int quartzUseSysBeep = 1;
63 int quartzServerVisible = FALSE;
64 int quartzDesiredDepth = -1;
65 int quartzHasRoot = FALSE, quartzEnableRootless = TRUE;
66 int quartzFullscreenDisableHotkeys = TRUE;
67 int noPseudoramiXExtension = FALSE;
68 int quartzXpluginOptions = 0;
72 static CGDirectDisplayID
73 display_at_index (int index)
77 CGDirectDisplayID dpy[index+1];
79 err = CGGetActiveDisplayList (index + 1, dpy, &cnt);
80 if (err == kCGErrorSuccess && (int) cnt == index + 1)
83 return kCGNullDirectDisplay;
87 display_screen_bounds (CGDirectDisplayID id, Bool remove_menubar)
91 frame = CGDisplayBounds (id);
93 if (remove_menubar && !quartzHasRoot
94 && frame.origin.x == 0 && frame.origin.y == 0)
96 /* Remove Aqua menubar from display bounds. */
99 frame.size.height -= 22;
106 addPseudoramiXScreens (int *x, int *y, int *width, int *height)
108 CGDisplayCount i, total = 16; /* FIXME: hardcoded maximum */
109 CGRect unionRect = CGRectNull, frame;
110 CGDirectDisplayID screens[total];
112 CGGetActiveDisplayList (total, screens, &total);
114 /* Get the union of all screens */
115 for (i = 0; i < total; i++)
117 CGDirectDisplayID dpy = screens[i];
119 /* we can't remove the menubar from the screen - doing so
120 would constrain the pointer to the screen, not allowing it
121 to reach the menubar.. */
123 frame = display_screen_bounds (dpy, FALSE);
124 unionRect = CGRectUnion (unionRect, frame);
127 /* Use unionRect as the screen size for the X server. */
128 *x = unionRect.origin.x;
129 *y = unionRect.origin.y;
130 *width = unionRect.size.width;
131 *height = unionRect.size.height;
133 /* Tell PseudoramiX about the real screens. */
134 for (i = 0; i < total; i++)
136 CGDirectDisplayID dpy = screens[i];
138 frame = display_screen_bounds (dpy, TRUE);
141 ErrorF("PseudoramiX screen %d added: %dx%d @ (%d,%d).\n", i,
142 (int)frame.size.width, (int)frame.size.height,
143 (int)frame.origin.x, (int)frame.origin.y);
146 frame.origin.x -= unionRect.origin.x;
147 frame.origin.y -= unionRect.origin.y;
150 ErrorF("PseudoramiX screen %d placed at X11 coordinate (%d,%d).\n",
151 i, (int)frame.origin.x, (int)frame.origin.y);
154 PseudoramiXAddScreen(frame.origin.x, frame.origin.y,
155 frame.size.width, frame.size.height);
159 /* Do mode dependent initialization of each screen for Quartz. */
161 QuartzAddScreen (int index, ScreenPtr pScreen)
163 DarwinFramebufferPtr dfb = SCREEN_PRIV(pScreen);
165 /* If no specific depth chosen, look for the depth of the main display.
166 Else if 16bpp specified, use that. Else use 32bpp. */
168 dfb->componentCount = 3;
169 dfb->bitsPerComponent = 8;
170 dfb->bitsPerPixel = 32;
172 if (quartzDesiredDepth == -1)
174 dfb->bitsPerComponent = CGDisplayBitsPerSample (kCGDirectMainDisplay);
175 dfb->bitsPerPixel = CGDisplayBitsPerPixel (kCGDirectMainDisplay);
177 else if (quartzDesiredDepth == 15)
179 dfb->bitsPerComponent = 5;
180 dfb->bitsPerPixel = 16;
182 else if (quartzDesiredDepth == 8)
184 dfb->bitsPerComponent = 8;
185 dfb->bitsPerPixel = 8;
186 dfb->componentCount = 1;
189 if (noPseudoramiXExtension)
191 CGDirectDisplayID dpy;
194 dpy = display_at_index (index);
196 frame = display_screen_bounds (dpy, TRUE);
198 dfb->x = frame.origin.x;
199 dfb->y = frame.origin.y;
200 dfb->width = frame.size.width;
201 dfb->height = frame.size.height;
205 addPseudoramiXScreens (&dfb->x, &dfb->y, &dfb->width, &dfb->height);
208 dfb->colorBitsPerPixel = dfb->bitsPerComponent * dfb->componentCount;
210 /* Passing zero width (pitch) makes miCreateScreenResources set the
211 screen pixmap to the framebuffer pointer, i.e. null. We'll take
214 dfb->framebuffer = NULL;
216 DRIScreenInit (pScreen);
221 /* Finalize mode specific setup of each screen. */
223 QuartzSetupScreen (int index, ScreenPtr pScreen)
225 // do full screen or rootless specific setup
226 if (! RootlessSetupScreen(index, pScreen))
229 // setup cursor support
230 if (! QuartzInitCursor(pScreen))
233 DRIFinishScreenInit (pScreen);
239 /* Quartz display initialization. */
241 QuartzInitOutput (int argc, char **argv)
243 static int orig_noPanoramiXExtension;
246 if (serverGeneration == 1) {
247 orig_noPanoramiXExtension = noPanoramiXExtension;
251 /* +xinerama option sets noPanoramiXExtension variable */
252 noPseudoramiXExtension = orig_noPanoramiXExtension;
254 total = 16; /* FIXME: hardcoded maximum */
257 CGDirectDisplayID screens[total];
258 CGGetActiveDisplayList (total, screens, &total);
261 if (noPseudoramiXExtension)
262 darwinScreensFound = total;
264 darwinScreensFound = 1; // only PseudoramiX knows about the rest
266 if (!quartzEnableRootless)
267 RootlessHideAllWindows ();
270 /* This function from randr.c */
271 extern char *ConnectionInfo;
272 static int padlength[4] = {0, 3, 2, 1};
274 RREditConnectionInfo (ScreenPtr pScreen)
276 xConnSetup *connSetup;
278 xPixmapFormat *formats;
285 connSetup = (xConnSetup *) ConnectionInfo;
286 vendor = (char *) connSetup + sizeof (xConnSetup);
287 formats = (xPixmapFormat *) ((char *) vendor +
288 connSetup->nbytesVendor +
289 padlength[connSetup->nbytesVendor & 3]);
290 root = (xWindowRoot *) ((char *) formats +
291 sizeof (xPixmapFormat) * screenInfo.numPixmapFormats);
292 while (screen != pScreen->myNum)
294 depth = (xDepth *) ((char *) root +
295 sizeof (xWindowRoot));
296 for (d = 0; d < root->nDepths; d++)
298 visual = (xVisualType *) ((char *) depth +
300 depth = (xDepth *) ((char *) visual +
301 depth->nVisuals * sizeof (xVisualType));
303 root = (xWindowRoot *) ((char *) depth);
306 root->pixWidth = pScreen->width;
307 root->pixHeight = pScreen->height;
308 root->mmWidth = pScreen->mmWidth;
309 root->mmHeight = pScreen->mmHeight;
313 QuartzUpdateScreens (void)
317 int x, y, width, height, sx, sy;
320 if (noPseudoramiXExtension || screenInfo.numScreens != 1)
322 /* FIXME: if not using Xinerama, we have multiple screens, and
323 to do this properly may need to add or remove screens. Which
324 isn't possible. So don't do anything. Another reason why
325 we default to running with Xinerama. */
330 pScreen = screenInfo.screens[0];
332 PseudoramiXResetScreens ();
333 addPseudoramiXScreens (&x, &y, &width, &height);
335 dixScreenOrigins[pScreen->myNum].x = x;
336 dixScreenOrigins[pScreen->myNum].y = y;
337 pScreen->mmWidth = pScreen->mmWidth * ((double) width / pScreen->width);
338 pScreen->mmHeight = pScreen->mmHeight * ((double) height / pScreen->height);
339 pScreen->width = width;
340 pScreen->height = height;
342 /* FIXME: should probably do something with RandR here. */
344 DarwinAdjustScreenOrigins (&screenInfo);
345 RootlessRepositionWindows (screenInfo.screens[0]);
346 RootlessUpdateScreenPixmap (screenInfo.screens[0]);
348 sx = dixScreenOrigins[pScreen->myNum].x + darwinMainScreenX;
349 sy = dixScreenOrigins[pScreen->myNum].y + darwinMainScreenY;
351 /* Adjust the root window. */
353 pRoot = WindowTable[pScreen->myNum];
354 pScreen->ResizeWindow (pRoot, x - sx, y - sy, width, height, NULL);
355 pScreen->PaintWindowBackground (pRoot, &pRoot->borderClip, PW_BACKGROUND);
356 QuartzIgnoreNextWarpCursor ();
357 DefineInitialRootWindow (pRoot);
359 /* Send an event for the root reconfigure */
361 e.u.u.type = ConfigureNotify;
362 e.u.configureNotify.window = pRoot->drawable.id;
363 e.u.configureNotify.aboveSibling = None;
364 e.u.configureNotify.x = x - sx;
365 e.u.configureNotify.y = y - sy;
366 e.u.configureNotify.width = width;
367 e.u.configureNotify.height = height;
368 e.u.configureNotify.borderWidth = wBorderWidth (pRoot);
369 e.u.configureNotify.override = pRoot->overrideRedirect;
370 DeliverEvents (pRoot, &e, 1, NullWindow);
372 /* FIXME: what does this do? */
373 RREditConnectionInfo (pScreen);
377 do_exec (void (*callback) (void *data), void *data)
379 /* Do the fork-twice trick to avoid needing to reap zombies */
381 int child1, child2 = 0;
384 /* we should really try to report errors here.. */
399 char buf[1024], *tem;
405 /* close all open files except for standard streams */
406 max_files = sysconf (_SC_OPEN_MAX);
407 for (i = 3; i < max_files; i++)
410 /* ensure stdin is on /dev/null */
412 open ("/dev/null", O_RDONLY);
415 tem = getenv ("HOME");
419 /* Setup environment */
420 snprintf (buf, sizeof (buf), ":%s", display);
421 setenv ("DISPLAY", buf, TRUE);
422 tem = getenv ("PATH");
423 if (tem != NULL && tem[0] != NULL)
424 snprintf (buf, sizeof (buf), "%s:/usr/X11R6/bin", tem);
426 snprintf (buf, sizeof (buf), "/bin:/usr/bin:/usr/X11R6/bin");
427 setenv ("PATH", buf, TRUE);
433 default: /* parent (child1) */
438 default: /* parent */
439 waitpid (child1, &status, 0);
444 run_client_callback (void *data)
447 execvp (argv[0], argv);
450 /* Note that this function is called from both X server and appkit threads */
452 QuartzRunClient (const char *command)
457 shell = getenv ("SHELL");
461 /* At least [ba]sh, [t]csh and zsh all work with this syntax. We
462 need to use an interactive shell to force it to load the user's
463 environment. Otherwise things like fink don't work at all well.. */
471 do_exec (run_client_callback, argv);
475 QuartzSetFullscreen (Bool state)
477 if (quartzHasRoot == state)
480 quartzHasRoot = state;
482 xp_disable_update ();
484 if (!quartzHasRoot && !quartzEnableRootless)
485 RootlessHideAllWindows ();
487 RootlessUpdateRooted (quartzHasRoot);
489 if (quartzHasRoot && !quartzEnableRootless)
490 RootlessShowAllWindows ();
492 /* Only update screen info when something is visible. Avoids the wm
493 moving the windows out from under the menubar when it shouldn't */
495 if (quartzHasRoot || quartzEnableRootless)
496 QuartzUpdateScreens ();
498 /* Somehow the menubar manages to interfere with our event stream
499 in fullscreen mode, even though it's not visible. */
501 X11ApplicationShowHideMenubar (!quartzHasRoot);
503 xp_reenable_update ();
505 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
506 if (quartzFullscreenDisableHotkeys)
507 xp_disable_hot_keys (quartzHasRoot);
512 QuartzSetRootless (Bool state)
514 if (quartzEnableRootless == state)
517 quartzEnableRootless = state;
519 if (!quartzEnableRootless && !quartzHasRoot)
521 xp_disable_update ();
522 RootlessHideAllWindows ();
523 xp_reenable_update ();
525 else if (quartzEnableRootless && !quartzHasRoot)
527 xp_disable_update ();
528 RootlessShowAllWindows ();
529 QuartzUpdateScreens ();
530 xp_reenable_update ();
534 /* Show the X server on screen. Does nothing if already shown. Restore the
535 X clip regions the X server cursor state. */
541 if (quartzServerVisible)
544 quartzServerVisible = TRUE;
546 for (i = 0; i < screenInfo.numScreens; i++)
548 if (screenInfo.screens[i])
549 QuartzResumeXCursor(screenInfo.screens[i]);
552 /* FIXME: not sure about this, it may need to have a preference like
555 if (!quartzEnableRootless)
556 QuartzSetFullscreen (TRUE);
559 /* Remove the X server display from the screen. Does nothing if already
560 hidden. Set X clip regions to prevent drawing, and restore the Aqua
567 if (!quartzServerVisible)
570 for (i = 0; i < screenInfo.numScreens; i++)
572 if (screenInfo.screens[i])
573 QuartzSuspendXCursor(screenInfo.screens[i]);
576 QuartzSetFullscreen (FALSE);
578 quartzServerVisible = FALSE;
581 /* Cleanup before X server shutdown. Release the screen and restore the
588 for (i = 0; i < screenInfo.numScreens; i++) {
589 if (screenInfo.screens[i]) {
590 QuartzSuspendXCursor(screenInfo.screens[i]);
596 QuartzProcessArgument( int argc, char *argv[], int i )
598 /* This arg is passed when launched from the Aqua GUI. */
599 if (strncmp (argv[i], "-psn_", 5) == 0)
604 if (strcmp (argv[i], "-depth") == 0)
609 FatalError ("-depth requires an argument\n");
611 arg = atoi (argv[i + 1]);
612 if (arg == 8 || arg == 15 || arg == 24)
613 quartzDesiredDepth = arg;
615 FatalError ("Only 8, 15 and 24 bit color depths are supported.\n");
624 QuartzClientMessage (const xEvent *xe)
626 switch (xe->u.clientMessage.u.l.type)
628 case kXquartzControllerNotify:
629 AppleWMSendEvent (AppleWMControllerNotify,
630 AppleWMControllerNotifyMask,
631 xe->u.clientMessage.u.l.longs0,
632 xe->u.clientMessage.u.l.longs1);
635 case kXquartzPasteboardNotify:
636 AppleWMSendEvent (AppleWMPasteboardNotify,
637 AppleWMPasteboardNotifyMask,
638 xe->u.clientMessage.u.l.longs0,
639 xe->u.clientMessage.u.l.longs1);
642 case kXquartzActivate:
644 AppleWMSendEvent (AppleWMActivationNotify,
645 AppleWMActivationNotifyMask,
649 case kXquartzDeactivate:
650 AppleWMSendEvent (AppleWMActivationNotify,
651 AppleWMActivationNotifyMask,
652 AppleWMIsInactive, 0);
656 case kXquartzDisplayChanged:
657 QuartzUpdateScreens ();
660 case kXquartzWindowState:
661 RootlessNativeWindowStateChanged (xe->u.clientMessage.u.l.longs0,
662 xe->u.clientMessage.u.l.longs1);
665 case kXquartzWindowMoved:
666 RootlessNativeWindowMoved (xe->u.clientMessage.u.l.longs0);
669 case kXquartzToggleFullscreen:
670 if (quartzEnableRootless)
671 QuartzSetFullscreen (!quartzHasRoot);
672 else if (quartzHasRoot)
678 case kXquartzSetRootless:
679 QuartzSetRootless (xe->u.clientMessage.u.l.longs0);
680 if (!quartzEnableRootless && !quartzHasRoot)