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