xprScreen.c revision c8548ba8
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        darwinScreensFound = 1;
288
289    if (xp_init(XP_BACKGROUND_EVENTS | XP_NO_DEFERRED_UPDATES) != Success)
290        FatalError("Could not initialize the Xplugin library.");
291
292    xp_select_events(XP_EVENT_DISPLAY_CHANGED
293                     | XP_EVENT_WINDOW_STATE_CHANGED
294                     | XP_EVENT_WINDOW_MOVED
295#ifdef XP_EVENT_SPACE_CHANGED
296                     | XP_EVENT_SPACE_CHANGED
297#endif
298                     | XP_EVENT_SURFACE_CHANGED
299                     | XP_EVENT_SURFACE_DESTROYED,
300                     eventHandler, NULL);
301
302    AppleDRIExtensionInit();
303    xprAppleWMInit();
304
305    XQuartzIsRootless = XQuartzRootlessDefault;
306    if (!XQuartzIsRootless)
307        RootlessHideAllWindows();
308}
309
310/*
311 * xprAddScreen
312 *  Init the framebuffer and record pixmap parameters for the screen.
313 */
314static Bool
315xprAddScreen(int index, ScreenPtr pScreen)
316{
317    DarwinFramebufferPtr dfb = SCREEN_PRIV(pScreen);
318    int depth = darwinDesiredDepth;
319
320    DEBUG_LOG("index=%d depth=%d\n", index, depth);
321
322    if (depth == -1) {
323        CGDisplayModeRef modeRef;
324        CFStringRef encStrRef;
325
326        modeRef = CGDisplayCopyDisplayMode(kCGDirectMainDisplay);
327        if (!modeRef)
328            goto have_depth;
329
330        encStrRef = CGDisplayModeCopyPixelEncoding(modeRef);
331        CFRelease(modeRef);
332        if (!encStrRef)
333            goto have_depth;
334
335        if (CFStringCompare(encStrRef, CFSTR(IO32BitDirectPixels),
336                            kCFCompareCaseInsensitive) ==
337            kCFCompareEqualTo) {
338            depth = 24;
339        }
340        else if (CFStringCompare(encStrRef, CFSTR(IO16BitDirectPixels),
341                                 kCFCompareCaseInsensitive) ==
342                 kCFCompareEqualTo) {
343            depth = 15;
344        }
345        else if (CFStringCompare(encStrRef, CFSTR(IO8BitIndexedPixels),
346                                 kCFCompareCaseInsensitive) ==
347                 kCFCompareEqualTo) {
348            depth = 8;
349        }
350
351        CFRelease(encStrRef);
352    }
353
354have_depth:
355    switch (depth) {
356    case 8:     // pseudo-working
357        dfb->visuals = PseudoColorMask;
358        dfb->preferredCVC = PseudoColor;
359        dfb->depth = 8;
360        dfb->bitsPerRGB = 8;
361        dfb->bitsPerPixel = 8;
362        dfb->redMask = 0;
363        dfb->greenMask = 0;
364        dfb->blueMask = 0;
365        break;
366
367#if 0
368    // Removed because Mountain Lion removed support for
369    // 15bit backing stores.  We can possibly re-add
370    // this once libXplugin is updated to work around it.
371    case 15:
372        dfb->visuals = TrueColorMask;     //LARGE_VISUALS;
373        dfb->preferredCVC = TrueColor;
374        dfb->depth = 15;
375        dfb->bitsPerRGB = 5;
376        dfb->bitsPerPixel = 16;
377        dfb->redMask = RM_ARGB(0, 5, 5, 5);
378        dfb->greenMask = GM_ARGB(0, 5, 5, 5);
379        dfb->blueMask = BM_ARGB(0, 5, 5, 5);
380        break;
381#endif
382
383    //        case 24:
384    default:
385        if (depth != 24)
386            ErrorF(
387                "Unsupported color depth requested.  Defaulting to 24bit. (depth=%d darwinDesiredDepth=%d)\n",
388                depth, darwinDesiredDepth);
389        dfb->visuals = TrueColorMask;     //LARGE_VISUALS;
390        dfb->preferredCVC = TrueColor;
391        dfb->depth = 24;
392        dfb->bitsPerRGB = 8;
393        dfb->bitsPerPixel = 32;
394        dfb->redMask = RM_ARGB(0, 8, 8, 8);
395        dfb->greenMask = GM_ARGB(0, 8, 8, 8);
396        dfb->blueMask = BM_ARGB(0, 8, 8, 8);
397        break;
398    }
399
400    if (noPseudoramiXExtension) {
401        CGDirectDisplayID dpy;
402        CGRect frame;
403
404        ErrorF("Warning: noPseudoramiXExtension!\n");
405
406        dpy = displayAtIndex(index);
407        QuartzCopyDisplayIDs(pScreen, 1, &dpy);
408
409        frame = displayScreenBounds(dpy);
410
411        dfb->x = frame.origin.x;
412        dfb->y = frame.origin.y;
413        dfb->width = frame.size.width;
414        dfb->height = frame.size.height;
415    }
416    else {
417        xprAddPseudoramiXScreens(&dfb->x, &dfb->y, &dfb->width, &dfb->height,
418                                 pScreen);
419    }
420
421    /* Passing zero width (pitch) makes miCreateScreenResources set the
422       screen pixmap to the framebuffer pointer, i.e. NULL. The generic
423       rootless code takes care of making this work. */
424    dfb->pitch = 0;
425    dfb->framebuffer = NULL;
426
427    DRIScreenInit(pScreen);
428
429    return TRUE;
430}
431
432/*
433 * xprSetupScreen
434 *  Setup the screen for rootless access.
435 */
436static Bool
437xprSetupScreen(int index, ScreenPtr pScreen)
438{
439#ifdef DAMAGE
440    // The Damage extension needs to wrap underneath the
441    // generic rootless layer, so do it now.
442    if (!DamageSetup(pScreen))
443        return FALSE;
444#endif
445
446    // Initialize generic rootless code
447    if (!xprInit(pScreen))
448        return FALSE;
449
450    return DRIFinishScreenInit(pScreen);
451}
452
453/*
454 * xprUpdateScreen
455 *  Update screen after configuration change.
456 */
457static void
458xprUpdateScreen(ScreenPtr pScreen)
459{
460    rootlessGlobalOffsetX = darwinMainScreenX;
461    rootlessGlobalOffsetY = darwinMainScreenY;
462
463    AppleWMSetScreenOrigin(pScreen->root);
464
465    RootlessRepositionWindows(pScreen);
466    RootlessUpdateScreenPixmap(pScreen);
467}
468
469/*
470 * xprInitInput
471 *  Finalize xpr specific setup.
472 */
473static void
474xprInitInput(int argc, char **argv)
475{
476    int i;
477
478    rootlessGlobalOffsetX = darwinMainScreenX;
479    rootlessGlobalOffsetY = darwinMainScreenY;
480
481    for (i = 0; i < screenInfo.numScreens; i++)
482        AppleWMSetScreenOrigin(screenInfo.screens[i]->root);
483}
484
485/*
486 * Quartz display mode function list.
487 */
488static QuartzModeProcsRec xprModeProcs = {
489    xprDisplayInit,
490    xprAddScreen,
491    xprSetupScreen,
492    xprInitInput,
493    QuartzInitCursor,
494    QuartzSuspendXCursor,
495    QuartzResumeXCursor,
496    xprAddPseudoramiXScreens,
497    xprUpdateScreen,
498    xprIsX11Window,
499    xprHideWindows,
500    RootlessFrameForWindow,
501    TopLevelParent,
502    DRICreateSurface,
503    DRIDestroySurface
504};
505
506/*
507 * QuartzModeBundleInit
508 *  Initialize the display mode bundle after loading.
509 */
510Bool
511QuartzModeBundleInit(void)
512{
513    quartzProcs = &xprModeProcs;
514    quartzOpenGLBundle = xprOpenGLBundle;
515    return TRUE;
516}
517