xprScreen.c revision 35c4bbdf
1/*
2 * Xplugin rootless implementation screen functions
3 *
4 * Copyright (c) 2002-2012 Apple Computer, Inc. All Rights Reserved.
5 * Copyright (c) 2004 Torrey T. Lyons. All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 *
25 * Except as contained in this notice, the name(s) of the above copyright
26 * holders shall not be used in advertising or otherwise to promote the sale,
27 * use or other dealings in this Software without prior written authorization.
28 */
29
30#include "sanitizedCarbon.h"
31
32#ifdef HAVE_DIX_CONFIG_H
33#include <dix-config.h>
34#endif
35
36#include "quartzCommon.h"
37#include "inputstr.h"
38#include "quartz.h"
39#include "quartzRandR.h"
40#include "xpr.h"
41#include "xprEvent.h"
42#include "pseudoramiX.h"
43#include "darwinEvents.h"
44#include "rootless.h"
45#include "dri.h"
46#include "globals.h"
47#include <Xplugin.h>
48#include "applewmExt.h"
49#include "micmap.h"
50
51#include "rootlessCommon.h"
52
53#ifdef DAMAGE
54#include "damage.h"
55#endif
56
57#include "nonsdk_extinit.h"
58
59#if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
60// From NSApplication.h
61extern const double NSAppKitVersionNumber;
62#endif
63
64/* 10.4's deferred update makes X slower.. have to live with the tearing
65 * for now.. */
66#define XP_NO_DEFERRED_UPDATES 8
67
68// Name of GLX bundle for native OpenGL
69static const char *xprOpenGLBundle = "glxCGL.bundle";
70
71/*
72 * eventHandler
73 *  Callback handler for Xplugin events.
74 */
75static void
76eventHandler(unsigned int type, const void *arg,
77             unsigned int arg_size, void *data)
78{
79
80    switch (type) {
81    case XP_EVENT_DISPLAY_CHANGED:
82        DEBUG_LOG("XP_EVENT_DISPLAY_CHANGED\n");
83        DarwinSendDDXEvent(kXquartzDisplayChanged, 0);
84        break;
85
86    case XP_EVENT_WINDOW_STATE_CHANGED:
87        if (arg_size >= sizeof(xp_window_state_event)) {
88            const xp_window_state_event *ws_arg = arg;
89
90            DEBUG_LOG("XP_EVENT_WINDOW_STATE_CHANGED: id=%d, state=%d\n",
91                      ws_arg->id,
92                      ws_arg->state);
93            DarwinSendDDXEvent(kXquartzWindowState, 2,
94                               ws_arg->id, ws_arg->state);
95        }
96        else {
97            DEBUG_LOG("XP_EVENT_WINDOW_STATE_CHANGED: ignored\n");
98        }
99        break;
100
101    case XP_EVENT_WINDOW_MOVED:
102        DEBUG_LOG("XP_EVENT_WINDOW_MOVED\n");
103        if (arg_size == sizeof(xp_window_id)) {
104            xp_window_id id = *(xp_window_id *)arg;
105            DarwinSendDDXEvent(kXquartzWindowMoved, 1, id);
106        }
107        break;
108
109    case XP_EVENT_SURFACE_DESTROYED:
110        DEBUG_LOG("XP_EVENT_SURFACE_DESTROYED\n");
111
112    case XP_EVENT_SURFACE_CHANGED:
113        DEBUG_LOG("XP_EVENT_SURFACE_CHANGED\n");
114        if (arg_size == sizeof(xp_surface_id)) {
115            int kind;
116
117            if (type == XP_EVENT_SURFACE_DESTROYED)
118                kind = AppleDRISurfaceNotifyDestroyed;
119            else
120                kind = AppleDRISurfaceNotifyChanged;
121
122            DRISurfaceNotify(*(xp_surface_id *)arg, kind);
123        }
124        break;
125
126#ifdef XP_EVENT_SPACE_CHANGED
127    case  XP_EVENT_SPACE_CHANGED:
128        DEBUG_LOG("XP_EVENT_SPACE_CHANGED\n");
129        if (arg_size == sizeof(uint32_t)) {
130            uint32_t space_id = *(uint32_t *)arg;
131            DarwinSendDDXEvent(kXquartzSpaceChanged, 1, space_id);
132        }
133        break;
134
135#endif
136    default:
137        ErrorF("Unknown XP_EVENT type (%d) in xprScreen:eventHandler\n", type);
138    }
139}
140
141/*
142 * displayAtIndex
143 *  Return the display ID for a particular display index.
144 */
145static CGDirectDisplayID
146displayAtIndex(int index)
147{
148    CGError err;
149    CGDisplayCount cnt;
150    CGDirectDisplayID dpy[index + 1];
151
152    err = CGGetActiveDisplayList(index + 1, dpy, &cnt);
153    if (err == kCGErrorSuccess && cnt == index + 1)
154        return dpy[index];
155    else
156        return kCGNullDirectDisplay;
157}
158
159/*
160 * displayScreenBounds
161 *  Return the bounds of a particular display.
162 */
163static CGRect
164displayScreenBounds(CGDirectDisplayID id)
165{
166    CGRect frame;
167
168    frame = CGDisplayBounds(id);
169
170    DEBUG_LOG("    %dx%d @ (%d,%d).\n",
171              (int)frame.size.width, (int)frame.size.height,
172              (int)frame.origin.x, (int)frame.origin.y);
173
174    Boolean spacePerDisplay = false;
175#if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
176    if (NSAppKitVersionNumber >= 1265)
177#endif
178    {
179        Boolean ok;
180        (void)CFPreferencesAppSynchronize(CFSTR("com.apple.spaces"));
181        spacePerDisplay = ! CFPreferencesGetAppBooleanValue(CFSTR("spans-displays"),
182                                                            CFSTR("com.apple.spaces"),
183                                                            &ok);
184        if (!ok)
185            spacePerDisplay = true;
186    }
187
188    /* Remove menubar to help standard X11 window managers.
189     * On Mavericks and later, the menu bar is on all displays when spans-displays is false or unset.
190     */
191    if (XQuartzIsRootless &&
192        (spacePerDisplay || (frame.origin.x == 0 && frame.origin.y == 0))) {
193        frame.origin.y += aquaMenuBarHeight;
194        frame.size.height -= aquaMenuBarHeight;
195    }
196
197    DEBUG_LOG("    %dx%d @ (%d,%d).\n",
198              (int)frame.size.width, (int)frame.size.height,
199              (int)frame.origin.x, (int)frame.origin.y);
200
201    return frame;
202}
203
204/*
205 * xprAddPseudoramiXScreens
206 *  Add a single virtual screen encompassing all the physical screens
207 *  with PseudoramiX.
208 */
209static void
210xprAddPseudoramiXScreens(int *x, int *y, int *width, int *height,
211                         ScreenPtr pScreen)
212{
213    CGDisplayCount i, displayCount;
214    CGDirectDisplayID *displayList = NULL;
215    CGRect unionRect = CGRectNull, frame;
216
217    // Find all the CoreGraphics displays
218    CGGetActiveDisplayList(0, NULL, &displayCount);
219    DEBUG_LOG("displayCount: %d\n", (int)displayCount);
220
221    if (!displayCount) {
222        ErrorF(
223            "CoreGraphics has reported no connected displays.  Creating a stub 800x600 display.\n");
224        *x = *y = 0;
225        *width = 800;
226        *height = 600;
227        PseudoramiXAddScreen(*x, *y, *width, *height);
228        QuartzCopyDisplayIDs(pScreen, 0, NULL);
229        return;
230    }
231
232    /* If the displays are captured, we are in a RandR game mode
233     * on the primary display, so we only want to include the first
234     * display.  The others are covered by the shield window.
235     */
236    if (CGDisplayIsCaptured(kCGDirectMainDisplay))
237        displayCount = 1;
238
239    displayList = malloc(displayCount * sizeof(CGDirectDisplayID));
240    if (!displayList)
241        FatalError("Unable to allocate memory for list of displays.\n");
242    CGGetActiveDisplayList(displayCount, displayList, &displayCount);
243    QuartzCopyDisplayIDs(pScreen, displayCount, displayList);
244
245    /* Get the union of all screens */
246    for (i = 0; i < displayCount; i++) {
247        CGDirectDisplayID dpy = displayList[i];
248        frame = displayScreenBounds(dpy);
249        unionRect = CGRectUnion(unionRect, frame);
250    }
251
252    /* Use unionRect as the screen size for the X server. */
253    *x = unionRect.origin.x;
254    *y = unionRect.origin.y;
255    *width = unionRect.size.width;
256    *height = unionRect.size.height;
257
258    DEBUG_LOG("  screen union origin: (%d,%d) size: (%d,%d).\n",
259              *x, *y, *width, *height);
260
261    /* Tell PseudoramiX about the real screens. */
262    for (i = 0; i < displayCount; i++) {
263        CGDirectDisplayID dpy = displayList[i];
264
265        frame = displayScreenBounds(dpy);
266        frame.origin.x -= unionRect.origin.x;
267        frame.origin.y -= unionRect.origin.y;
268
269        DEBUG_LOG("    placed at X11 coordinate (%d,%d).\n",
270                  (int)frame.origin.x, (int)frame.origin.y);
271
272        PseudoramiXAddScreen(frame.origin.x, frame.origin.y,
273                             frame.size.width, frame.size.height);
274    }
275
276    free(displayList);
277}
278
279/*
280 * xprDisplayInit
281 *  Find number of CoreGraphics displays and initialize Xplugin.
282 */
283static void
284xprDisplayInit(void)
285{
286    CGDisplayCount displayCount;
287
288    TRACE();
289
290    CGGetActiveDisplayList(0, NULL, &displayCount);
291
292    /* With PseudoramiX, the X server only sees one screen; only PseudoramiX
293       itself knows about all of the screens. */
294
295    if (noPseudoramiXExtension)
296        darwinScreensFound = displayCount;
297    else
298        darwinScreensFound = 1;
299
300    if (xp_init(XP_BACKGROUND_EVENTS | XP_NO_DEFERRED_UPDATES) != Success)
301        FatalError("Could not initialize the Xplugin library.");
302
303    xp_select_events(XP_EVENT_DISPLAY_CHANGED
304                     | XP_EVENT_WINDOW_STATE_CHANGED
305                     | XP_EVENT_WINDOW_MOVED
306#ifdef XP_EVENT_SPACE_CHANGED
307                     | XP_EVENT_SPACE_CHANGED
308#endif
309                     | XP_EVENT_SURFACE_CHANGED
310                     | XP_EVENT_SURFACE_DESTROYED,
311                     eventHandler, NULL);
312
313    AppleDRIExtensionInit();
314    xprAppleWMInit();
315
316    XQuartzIsRootless = XQuartzRootlessDefault;
317    if (!XQuartzIsRootless)
318        RootlessHideAllWindows();
319}
320
321/*
322 * xprAddScreen
323 *  Init the framebuffer and record pixmap parameters for the screen.
324 */
325static Bool
326xprAddScreen(int index, ScreenPtr pScreen)
327{
328    DarwinFramebufferPtr dfb = SCREEN_PRIV(pScreen);
329    int depth = darwinDesiredDepth;
330
331    DEBUG_LOG("index=%d depth=%d\n", index, depth);
332
333    if (depth == -1) {
334#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
335        depth = CGDisplaySamplesPerPixel(kCGDirectMainDisplay) *
336                CGDisplayBitsPerSample(kCGDirectMainDisplay);
337#else
338        CGDisplayModeRef modeRef;
339        CFStringRef encStrRef;
340
341        modeRef = CGDisplayCopyDisplayMode(kCGDirectMainDisplay);
342        if (!modeRef)
343            goto have_depth;
344
345        encStrRef = CGDisplayModeCopyPixelEncoding(modeRef);
346        CFRelease(modeRef);
347        if (!encStrRef)
348            goto have_depth;
349
350        if (CFStringCompare(encStrRef, CFSTR(IO32BitDirectPixels),
351                            kCFCompareCaseInsensitive) ==
352            kCFCompareEqualTo) {
353            depth = 24;
354        }
355        else if (CFStringCompare(encStrRef, CFSTR(IO16BitDirectPixels),
356                                 kCFCompareCaseInsensitive) ==
357                 kCFCompareEqualTo) {
358            depth = 15;
359        }
360        else if (CFStringCompare(encStrRef, CFSTR(IO8BitIndexedPixels),
361                                 kCFCompareCaseInsensitive) ==
362                 kCFCompareEqualTo) {
363            depth = 8;
364        }
365
366        CFRelease(encStrRef);
367#endif
368    }
369
370#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
371have_depth:
372#endif
373    switch (depth) {
374    case 8:     // pseudo-working
375        dfb->visuals = PseudoColorMask;
376        dfb->preferredCVC = PseudoColor;
377        dfb->depth = 8;
378        dfb->bitsPerRGB = 8;
379        dfb->bitsPerPixel = 8;
380        dfb->redMask = 0;
381        dfb->greenMask = 0;
382        dfb->blueMask = 0;
383        break;
384
385#if 0
386    // Removed because Mountain Lion removed support for
387    // 15bit backing stores.  We can possibly re-add
388    // this once libXplugin is updated to work around it.
389    case 15:
390        dfb->visuals = TrueColorMask;     //LARGE_VISUALS;
391        dfb->preferredCVC = TrueColor;
392        dfb->depth = 15;
393        dfb->bitsPerRGB = 5;
394        dfb->bitsPerPixel = 16;
395        dfb->redMask = RM_ARGB(0, 5, 5, 5);
396        dfb->greenMask = GM_ARGB(0, 5, 5, 5);
397        dfb->blueMask = BM_ARGB(0, 5, 5, 5);
398        break;
399#endif
400
401    //        case 24:
402    default:
403        if (depth != 24)
404            ErrorF(
405                "Unsupported color depth requested.  Defaulting to 24bit. (depth=%d darwinDesiredDepth=%d)\n",
406                depth, darwinDesiredDepth);
407        dfb->visuals = TrueColorMask;     //LARGE_VISUALS;
408        dfb->preferredCVC = TrueColor;
409        dfb->depth = 24;
410        dfb->bitsPerRGB = 8;
411        dfb->bitsPerPixel = 32;
412        dfb->redMask = RM_ARGB(0, 8, 8, 8);
413        dfb->greenMask = GM_ARGB(0, 8, 8, 8);
414        dfb->blueMask = BM_ARGB(0, 8, 8, 8);
415        break;
416    }
417
418    if (noPseudoramiXExtension) {
419        CGDirectDisplayID dpy;
420        CGRect frame;
421
422        ErrorF("Warning: noPseudoramiXExtension!\n");
423
424        dpy = displayAtIndex(index);
425        QuartzCopyDisplayIDs(pScreen, 1, &dpy);
426
427        frame = displayScreenBounds(dpy);
428
429        dfb->x = frame.origin.x;
430        dfb->y = frame.origin.y;
431        dfb->width = frame.size.width;
432        dfb->height = frame.size.height;
433    }
434    else {
435        xprAddPseudoramiXScreens(&dfb->x, &dfb->y, &dfb->width, &dfb->height,
436                                 pScreen);
437    }
438
439    /* Passing zero width (pitch) makes miCreateScreenResources set the
440       screen pixmap to the framebuffer pointer, i.e. NULL. The generic
441       rootless code takes care of making this work. */
442    dfb->pitch = 0;
443    dfb->framebuffer = NULL;
444
445    DRIScreenInit(pScreen);
446
447    return TRUE;
448}
449
450/*
451 * xprSetupScreen
452 *  Setup the screen for rootless access.
453 */
454static Bool
455xprSetupScreen(int index, ScreenPtr pScreen)
456{
457#ifdef DAMAGE
458    // The Damage extension needs to wrap underneath the
459    // generic rootless layer, so do it now.
460    if (!DamageSetup(pScreen))
461        return FALSE;
462#endif
463
464    // Initialize generic rootless code
465    if (!xprInit(pScreen))
466        return FALSE;
467
468    return DRIFinishScreenInit(pScreen);
469}
470
471/*
472 * xprUpdateScreen
473 *  Update screen after configuation change.
474 */
475static void
476xprUpdateScreen(ScreenPtr pScreen)
477{
478    rootlessGlobalOffsetX = darwinMainScreenX;
479    rootlessGlobalOffsetY = darwinMainScreenY;
480
481    AppleWMSetScreenOrigin(pScreen->root);
482
483    RootlessRepositionWindows(pScreen);
484    RootlessUpdateScreenPixmap(pScreen);
485}
486
487/*
488 * xprInitInput
489 *  Finalize xpr specific setup.
490 */
491static void
492xprInitInput(int argc, char **argv)
493{
494    int i;
495
496    rootlessGlobalOffsetX = darwinMainScreenX;
497    rootlessGlobalOffsetY = darwinMainScreenY;
498
499    for (i = 0; i < screenInfo.numScreens; i++)
500        AppleWMSetScreenOrigin(screenInfo.screens[i]->root);
501}
502
503/*
504 * Quartz display mode function list.
505 */
506static QuartzModeProcsRec xprModeProcs = {
507    xprDisplayInit,
508    xprAddScreen,
509    xprSetupScreen,
510    xprInitInput,
511    QuartzInitCursor,
512    QuartzSuspendXCursor,
513    QuartzResumeXCursor,
514    xprAddPseudoramiXScreens,
515    xprUpdateScreen,
516    xprIsX11Window,
517    xprHideWindows,
518    RootlessFrameForWindow,
519    TopLevelParent,
520    DRICreateSurface,
521    DRIDestroySurface
522};
523
524/*
525 * QuartzModeBundleInit
526 *  Initialize the display mode bundle after loading.
527 */
528Bool
529QuartzModeBundleInit(void)
530{
531    quartzProcs = &xprModeProcs;
532    quartzOpenGLBundle = xprOpenGLBundle;
533    return TRUE;
534}
535