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
63 static const char *xprOpenGLBundle = "glxCGL.bundle";
64 
65 /*
66  * eventHandler
67  *  Callback handler for Xplugin events.
68  */
69 static void
eventHandler(unsigned int type,const void * arg,unsigned int arg_size,void * data)70 eventHandler(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  */
139 static CGDirectDisplayID
displayAtIndex(int index)140 displayAtIndex(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  */
157 static CGRect
displayScreenBounds(CGDirectDisplayID id)158 displayScreenBounds(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  */
198 static void
xprAddPseudoramiXScreens(int * x,int * y,int * width,int * height,ScreenPtr pScreen)199 xprAddPseudoramiXScreens(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  */
272 static void
xprDisplayInit(void)273 xprDisplayInit(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  */
314 static Bool
xprAddScreen(int index,ScreenPtr pScreen)315 xprAddScreen(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 
354 have_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  */
436 static Bool
xprSetupScreen(int index,ScreenPtr pScreen)437 xprSetupScreen(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  */
457 static void
xprUpdateScreen(ScreenPtr pScreen)458 xprUpdateScreen(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  */
473 static void
xprInitInput(int argc,char ** argv)474 xprInitInput(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  */
488 static 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  */
510 Bool
QuartzModeBundleInit(void)511 QuartzModeBundleInit(void)
512 {
513     quartzProcs = &xprModeProcs;
514     quartzOpenGLBundle = xprOpenGLBundle;
515     return TRUE;
516 }
517