quartz.c revision 8223e2f2
1/*
2 *
3 * Quartz-specific support for the Darwin X Server
4 *
5 * Copyright (c) 2001-2004 Greg Parker and Torrey T. Lyons.
6 *                 All Rights Reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
22 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
25 *
26 * Except as contained in this notice, the name(s) of the above copyright
27 * holders shall not be used in advertising or otherwise to promote the sale,
28 * use or other dealings in this Software without prior written authorization.
29 */
30
31#include "sanitizedCarbon.h"
32
33#ifdef HAVE_DIX_CONFIG_H
34#include <dix-config.h>
35#endif
36
37#include "quartzCommon.h"
38#include "quartzRandR.h"
39#include "inputstr.h"
40#include "quartz.h"
41#include "darwin.h"
42#include "darwinEvents.h"
43#include "pseudoramiX.h"
44#define _APPLEWM_SERVER_
45#include "applewmExt.h"
46
47#include "X11Application.h"
48
49#include <X11/extensions/applewmconst.h>
50
51// X headers
52#include "scrnintstr.h"
53#include "windowstr.h"
54#include "colormapst.h"
55#include "globals.h"
56#include "mi.h"
57
58// System headers
59#include <stdlib.h>
60#include <string.h>
61#include <sys/types.h>
62#include <sys/stat.h>
63#include <fcntl.h>
64#include <IOKit/pwr_mgt/IOPMLib.h>
65#include <pthread.h>
66#include <signal.h>
67
68#include <rootlessCommon.h>
69#include <Xplugin.h>
70
71DevPrivateKeyRec        quartzScreenKeyRec;
72int                     aquaMenuBarHeight = 0;
73QuartzModeProcsPtr      quartzProcs = NULL;
74const char             *quartzOpenGLBundle = NULL;
75
76Bool XQuartzFullscreenDisableHotkeys = TRUE;
77Bool XQuartzOptionSendsAlt = FALSE;
78Bool XQuartzEnableKeyEquivalents = TRUE;
79Bool XQuartzFullscreenVisible = FALSE;
80Bool XQuartzRootlessDefault = TRUE;
81Bool XQuartzIsRootless = TRUE;
82Bool XQuartzServerVisible = FALSE;
83Bool XQuartzFullscreenMenu = FALSE;
84
85int32_t XQuartzShieldingWindowLevel = 0;
86
87/*
88===========================================================================
89
90 Screen functions
91
92===========================================================================
93*/
94
95/*
96 * QuartzAddScreen
97 *  Do mode dependent initialization of each screen for Quartz.
98 */
99Bool QuartzAddScreen(
100    int index,
101    ScreenPtr pScreen)
102{
103    // allocate space for private per screen Quartz specific storage
104    QuartzScreenPtr displayInfo = calloc(sizeof(QuartzScreenRec), 1);
105
106    // QUARTZ_PRIV(pScreen) = displayInfo;
107    dixSetPrivate(&pScreen->devPrivates, quartzScreenKey, displayInfo);
108
109    // do Quartz mode specific initialization
110    return quartzProcs->AddScreen(index, pScreen);
111}
112
113
114/*
115 * QuartzSetupScreen
116 *  Finalize mode specific setup of each screen.
117 */
118Bool QuartzSetupScreen(
119    int index,
120    ScreenPtr pScreen)
121{
122    // do Quartz mode specific setup
123    if (! quartzProcs->SetupScreen(index, pScreen))
124        return FALSE;
125
126    // setup cursor support
127    if (! quartzProcs->InitCursor(pScreen))
128        return FALSE;
129
130#if defined(RANDR)
131    if(!QuartzRandRInit(pScreen)) {
132        DEBUG_LOG("Failed to init RandR extension.\n");
133        return FALSE;
134    }
135#endif
136
137    return TRUE;
138}
139
140
141/*
142 * QuartzInitOutput
143 *  Quartz display initialization.
144 */
145void QuartzInitOutput(
146    int argc,
147    char **argv )
148{
149    /* For XQuartz, we want to just use the default signal handler to work better with CrashTracer */
150    signal(SIGSEGV, SIG_DFL);
151    signal(SIGILL, SIG_DFL);
152#ifdef SIGEMT
153    signal(SIGEMT, SIG_DFL);
154#endif
155    signal(SIGFPE, SIG_DFL);
156#ifdef SIGBUS
157    signal(SIGBUS, SIG_DFL);
158#endif
159#ifdef SIGSYS
160    signal(SIGSYS, SIG_DFL);
161#endif
162#ifdef SIGXCPU
163    signal(SIGXCPU, SIG_DFL);
164#endif
165#ifdef SIGXFSZ
166    signal(SIGXFSZ, SIG_DFL);
167#endif
168
169    if (!RegisterBlockAndWakeupHandlers(QuartzBlockHandler,
170                                        QuartzWakeupHandler,
171                                        NULL))
172    {
173        FatalError("Could not register block and wakeup handlers.");
174    }
175
176    if (!dixRegisterPrivateKey(&quartzScreenKeyRec, PRIVATE_SCREEN, 0))
177	FatalError("Failed to alloc quartz screen private.\n");
178
179    // Do display mode specific initialization
180    quartzProcs->DisplayInit();
181}
182
183
184/*
185 * QuartzInitInput
186 *  Inform the main thread the X server is ready to handle events.
187 */
188void QuartzInitInput(
189    int argc,
190    char **argv )
191{
192    X11ApplicationSetCanQuit(0);
193    X11ApplicationServerReady();
194    // Do final display mode specific initialization before handling events
195    if (quartzProcs->InitInput)
196        quartzProcs->InitInput(argc, argv);
197}
198
199
200void QuartzUpdateScreens(void) {
201    ScreenPtr pScreen;
202    WindowPtr pRoot;
203    int x, y, width, height, sx, sy;
204    xEvent e;
205    BoxRec bounds;
206
207    if (noPseudoramiXExtension || screenInfo.numScreens != 1)
208    {
209        /* FIXME: if not using Xinerama, we have multiple screens, and
210         to do this properly may need to add or remove screens. Which
211         isn't possible. So don't do anything. Another reason why
212         we default to running with Xinerama. */
213
214        return;
215    }
216
217    pScreen = screenInfo.screens[0];
218
219    PseudoramiXResetScreens();
220    quartzProcs->AddPseudoramiXScreens(&x, &y, &width, &height, pScreen);
221
222    pScreen->x = x;
223    pScreen->y = y;
224    pScreen->mmWidth = pScreen->mmWidth * ((double) width / pScreen->width);
225    pScreen->mmHeight = pScreen->mmHeight * ((double) height / pScreen->height);
226    pScreen->width = width;
227    pScreen->height = height;
228
229    DarwinAdjustScreenOrigins(&screenInfo);
230
231    /* DarwinAdjustScreenOrigins or UpdateScreen may change pScreen->x/y,
232     * so use it rather than x/y
233     */
234    sx = pScreen->x + darwinMainScreenX;
235    sy = pScreen->y + darwinMainScreenY;
236
237    /* Adjust the root window. */
238    pRoot = pScreen->root;
239    AppleWMSetScreenOrigin(pRoot);
240    pScreen->ResizeWindow(pRoot, x - sx, y - sy, width, height, NULL);
241
242    miPaintWindow(pRoot, &pRoot->borderClip,  PW_BACKGROUND);
243
244    /* <rdar://problem/7770779> pointer events are clipped to old display region after display reconfiguration
245     * http://xquartz.macosforge.org/trac/ticket/346
246     */
247    bounds.x1 = 0;
248    bounds.x2 = width;
249    bounds.y1 = 0;
250    bounds.y2 = height;
251    pScreen->ConstrainCursor(inputInfo.pointer, pScreen, &bounds);
252    inputInfo.pointer->spriteInfo->sprite->physLimits = bounds;
253    inputInfo.pointer->spriteInfo->sprite->hotLimits = bounds;
254
255    DEBUG_LOG("Root Window: %dx%d @ (%d, %d) darwinMainScreen (%d, %d) xy (%d, %d) dixScreenOrigins (%d, %d)\n", width, height, x - sx, y - sy, darwinMainScreenX, darwinMainScreenY, x, y, pScreen->x, pScreen->y);
256
257    /* Send an event for the root reconfigure */
258    e.u.u.type = ConfigureNotify;
259    e.u.configureNotify.window = pRoot->drawable.id;
260    e.u.configureNotify.aboveSibling = None;
261    e.u.configureNotify.x = x - sx;
262    e.u.configureNotify.y = y - sy;
263    e.u.configureNotify.width = width;
264    e.u.configureNotify.height = height;
265    e.u.configureNotify.borderWidth = wBorderWidth(pRoot);
266    e.u.configureNotify.override = pRoot->overrideRedirect;
267    DeliverEvents(pRoot, &e, 1, NullWindow);
268
269    quartzProcs->UpdateScreen(pScreen);
270
271    /* Tell RandR about the new size, so new connections get the correct info */
272    RRScreenSizeNotify(pScreen);
273}
274
275static void pokeActivityCallback(CFRunLoopTimerRef timer, void *info) {
276    UpdateSystemActivity(OverallAct);
277}
278
279static void QuartzScreenSaver(int state) {
280    static CFRunLoopTimerRef pokeActivityTimer = NULL;
281    static CFRunLoopTimerContext pokeActivityContext = { 0, NULL, NULL, NULL, NULL };
282    static pthread_mutex_t pokeActivityMutex = PTHREAD_MUTEX_INITIALIZER;
283
284    pthread_mutex_lock(&pokeActivityMutex);
285
286    if(state) {
287        if(pokeActivityTimer == NULL)
288            goto QuartzScreenSaverEnd;
289
290        CFRunLoopTimerInvalidate(pokeActivityTimer);
291        CFRelease(pokeActivityTimer);
292        pokeActivityTimer = NULL;
293    } else {
294        if(pokeActivityTimer != NULL)
295            goto QuartzScreenSaverEnd;
296
297        pokeActivityTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 30, 0, 0, pokeActivityCallback, &pokeActivityContext);
298        if(pokeActivityTimer == NULL) {
299            ErrorF("Unable to create pokeActivityTimer.\n");
300            goto QuartzScreenSaverEnd;
301        }
302
303        CFRunLoopAddTimer(CFRunLoopGetMain(), pokeActivityTimer, kCFRunLoopCommonModes);
304    }
305QuartzScreenSaverEnd:
306    pthread_mutex_unlock(&pokeActivityMutex);
307}
308
309void QuartzShowFullscreen(int state) {
310    int i;
311
312    DEBUG_LOG("QuartzShowFullscreen: state=%d\n", state);
313
314    if(XQuartzIsRootless) {
315        ErrorF("QuartzShowFullscreen called while in rootless mode.\n");
316        return;
317    }
318
319    QuartzScreenSaver(!state);
320
321    if(XQuartzFullscreenVisible == state)
322        return;
323
324    XQuartzFullscreenVisible = state;
325
326    xp_disable_update ();
327
328    if (!XQuartzFullscreenVisible)
329        RootlessHideAllWindows();
330
331    RootlessUpdateRooted(XQuartzFullscreenVisible);
332
333    if (XQuartzFullscreenVisible) {
334        RootlessShowAllWindows ();
335        for (i=0; i < screenInfo.numScreens; i++) {
336            ScreenPtr pScreen = screenInfo.screens[i];
337            RootlessRepositionWindows(pScreen);
338            // JH: I don't think this is necessary, but keeping it here as a reminder
339            //RootlessUpdateScreenPixmap(pScreen);
340        }
341    }
342
343    /* Somehow the menubar manages to interfere with our event stream
344     * in fullscreen mode, even though it's not visible.
345     */
346    X11ApplicationShowHideMenubar(!XQuartzFullscreenVisible);
347
348    xp_reenable_update ();
349
350    if (XQuartzFullscreenDisableHotkeys)
351        xp_disable_hot_keys(XQuartzFullscreenVisible);
352}
353
354void QuartzSetRootless(Bool state) {
355    DEBUG_LOG("QuartzSetRootless state=%d\n", state);
356
357    if(XQuartzIsRootless == state)
358        return;
359
360    if(state)
361        QuartzShowFullscreen(FALSE);
362
363    XQuartzIsRootless = state;
364
365    xp_disable_update();
366
367    /* When in rootless, the menubar is not part of the screen, so we need to update our screens on toggle */
368    QuartzUpdateScreens();
369
370    if(XQuartzIsRootless) {
371        RootlessShowAllWindows();
372    } else {
373        RootlessHideAllWindows();
374    }
375
376    X11ApplicationShowHideMenubar(TRUE);
377
378    xp_reenable_update();
379
380    xp_disable_hot_keys(FALSE);
381}
382
383/*
384 * QuartzShow
385 *  Show the X server on screen. Does nothing if already shown.
386 *  Calls mode specific screen resume to restore the X clip regions
387 *  (if needed) and the X server cursor state.
388 */
389void QuartzShow(void) {
390    int i;
391
392    if (XQuartzServerVisible)
393        return;
394
395    XQuartzServerVisible = TRUE;
396    for (i = 0; i < screenInfo.numScreens; i++) {
397        if (screenInfo.screens[i]) {
398            quartzProcs->ResumeScreen(screenInfo.screens[i]);
399        }
400    }
401
402    if (!XQuartzIsRootless)
403        QuartzShowFullscreen(TRUE);
404}
405
406
407/*
408 * QuartzHide
409 *  Remove the X server display from the screen. Does nothing if already
410 *  hidden. Calls mode specific screen suspend to set X clip regions to
411 *  prevent drawing (if needed) and restore the Aqua cursor.
412 */
413void QuartzHide(void)
414{
415    int i;
416
417    if (XQuartzServerVisible) {
418        for (i = 0; i < screenInfo.numScreens; i++) {
419            if (screenInfo.screens[i]) {
420                quartzProcs->SuspendScreen(screenInfo.screens[i]);
421            }
422        }
423    }
424
425    if(!XQuartzIsRootless)
426        QuartzShowFullscreen(FALSE);
427    XQuartzServerVisible = FALSE;
428}
429
430
431/*
432 * QuartzSetRootClip
433 *  Enable or disable rendering to the X screen.
434 */
435void QuartzSetRootClip(
436    BOOL enable)
437{
438    int i;
439
440    if (!XQuartzServerVisible)
441        return;
442
443    for (i = 0; i < screenInfo.numScreens; i++) {
444        if (screenInfo.screens[i]) {
445            xf86SetRootClip(screenInfo.screens[i], enable);
446        }
447    }
448}
449
450/*
451 * QuartzSpaceChanged
452 *  Unmap offscreen windows, map onscreen windows
453 */
454void QuartzSpaceChanged(uint32_t space_id) {
455    /* Do something special here, so we don't depend on quartz-wm for spaces to work... */
456    DEBUG_LOG("Space Changed (%u) ... do something interesting...\n", space_id);
457}
458
459/*
460 * QuartzCopyDisplayIDs
461 *  Associate an X11 screen with one or more CoreGraphics display IDs by copying
462 *  the list into a private array. Free the previously copied array, if present.
463 */
464void QuartzCopyDisplayIDs(ScreenPtr pScreen,
465                          int displayCount, CGDirectDisplayID *displayIDs) {
466    QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
467    int size = displayCount * sizeof(CGDirectDisplayID);
468
469    free(pQuartzScreen->displayIDs);
470    pQuartzScreen->displayIDs = malloc(size);
471    memcpy(pQuartzScreen->displayIDs, displayIDs, size);
472    pQuartzScreen->displayCount = displayCount;
473}
474
475void NSBeep(void);
476void DDXRingBell(
477    int volume,         // volume is % of max
478    int pitch,          // pitch is Hz
479    int duration)       // duration is milliseconds
480{
481    if (volume)
482        NSBeep();
483}
484