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    /* <rdar://problem/7770779> pointer events are clipped to old display region after display reconfiguration
243     * http://xquartz.macosforge.org/trac/ticket/346
244     */
245    bounds.x1 = 0;
246    bounds.x2 = width;
247    bounds.y1 = 0;
248    bounds.y2 = height;
249    pScreen->ConstrainCursor(inputInfo.pointer, pScreen, &bounds);
250    inputInfo.pointer->spriteInfo->sprite->physLimits = bounds;
251    inputInfo.pointer->spriteInfo->sprite->hotLimits = bounds;
252
253    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);
254
255    /* Send an event for the root reconfigure */
256    e.u.u.type = ConfigureNotify;
257    e.u.configureNotify.window = pRoot->drawable.id;
258    e.u.configureNotify.aboveSibling = None;
259    e.u.configureNotify.x = x - sx;
260    e.u.configureNotify.y = y - sy;
261    e.u.configureNotify.width = width;
262    e.u.configureNotify.height = height;
263    e.u.configureNotify.borderWidth = wBorderWidth(pRoot);
264    e.u.configureNotify.override = pRoot->overrideRedirect;
265    DeliverEvents(pRoot, &e, 1, NullWindow);
266
267    quartzProcs->UpdateScreen(pScreen);
268
269    /* miPaintWindow needs to be called after RootlessUpdateScreenPixmap (from xprUpdateScreen) */
270    miPaintWindow(pRoot, &pRoot->borderClip,  PW_BACKGROUND);
271
272    /* Tell RandR about the new size, so new connections get the correct info */
273    RRScreenSizeNotify(pScreen);
274}
275
276static void pokeActivityCallback(CFRunLoopTimerRef timer, void *info) {
277    UpdateSystemActivity(OverallAct);
278}
279
280static void QuartzScreenSaver(int state) {
281    static CFRunLoopTimerRef pokeActivityTimer = NULL;
282    static CFRunLoopTimerContext pokeActivityContext = { 0, NULL, NULL, NULL, NULL };
283    static pthread_mutex_t pokeActivityMutex = PTHREAD_MUTEX_INITIALIZER;
284
285    pthread_mutex_lock(&pokeActivityMutex);
286
287    if(state) {
288        if(pokeActivityTimer == NULL)
289            goto QuartzScreenSaverEnd;
290
291        CFRunLoopTimerInvalidate(pokeActivityTimer);
292        CFRelease(pokeActivityTimer);
293        pokeActivityTimer = NULL;
294    } else {
295        if(pokeActivityTimer != NULL)
296            goto QuartzScreenSaverEnd;
297
298        pokeActivityTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 30, 0, 0, pokeActivityCallback, &pokeActivityContext);
299        if(pokeActivityTimer == NULL) {
300            ErrorF("Unable to create pokeActivityTimer.\n");
301            goto QuartzScreenSaverEnd;
302        }
303
304        CFRunLoopAddTimer(CFRunLoopGetMain(), pokeActivityTimer, kCFRunLoopCommonModes);
305    }
306QuartzScreenSaverEnd:
307    pthread_mutex_unlock(&pokeActivityMutex);
308}
309
310void QuartzShowFullscreen(int state) {
311    int i;
312
313    DEBUG_LOG("QuartzShowFullscreen: state=%d\n", state);
314
315    if(XQuartzIsRootless) {
316        ErrorF("QuartzShowFullscreen called while in rootless mode.\n");
317        return;
318    }
319
320    QuartzScreenSaver(!state);
321
322    if(XQuartzFullscreenVisible == state)
323        return;
324
325    XQuartzFullscreenVisible = state;
326
327    xp_disable_update ();
328
329    if (!XQuartzFullscreenVisible)
330        RootlessHideAllWindows();
331
332    RootlessUpdateRooted(XQuartzFullscreenVisible);
333
334    if (XQuartzFullscreenVisible) {
335        RootlessShowAllWindows ();
336        for (i=0; i < screenInfo.numScreens; i++) {
337            ScreenPtr pScreen = screenInfo.screens[i];
338            RootlessRepositionWindows(pScreen);
339            // JH: I don't think this is necessary, but keeping it here as a reminder
340            //RootlessUpdateScreenPixmap(pScreen);
341        }
342    }
343
344    /* Somehow the menubar manages to interfere with our event stream
345     * in fullscreen mode, even though it's not visible.
346     */
347    X11ApplicationShowHideMenubar(!XQuartzFullscreenVisible);
348
349    xp_reenable_update ();
350
351    if (XQuartzFullscreenDisableHotkeys)
352        xp_disable_hot_keys(XQuartzFullscreenVisible);
353}
354
355void QuartzSetRootless(Bool state) {
356    DEBUG_LOG("QuartzSetRootless state=%d\n", state);
357
358    if(XQuartzIsRootless == state)
359        return;
360
361    if(state)
362        QuartzShowFullscreen(FALSE);
363
364    XQuartzIsRootless = state;
365
366    xp_disable_update();
367
368    /* When in rootless, the menubar is not part of the screen, so we need to update our screens on toggle */
369    QuartzUpdateScreens();
370
371    if(XQuartzIsRootless) {
372        RootlessShowAllWindows();
373    } else {
374        RootlessHideAllWindows();
375    }
376
377    X11ApplicationShowHideMenubar(TRUE);
378
379    xp_reenable_update();
380
381    xp_disable_hot_keys(FALSE);
382}
383
384/*
385 * QuartzShow
386 *  Show the X server on screen. Does nothing if already shown.
387 *  Calls mode specific screen resume to restore the X clip regions
388 *  (if needed) and the X server cursor state.
389 */
390void QuartzShow(void) {
391    int i;
392
393    if (XQuartzServerVisible)
394        return;
395
396    XQuartzServerVisible = TRUE;
397    for (i = 0; i < screenInfo.numScreens; i++) {
398        if (screenInfo.screens[i]) {
399            quartzProcs->ResumeScreen(screenInfo.screens[i]);
400        }
401    }
402
403    if (!XQuartzIsRootless)
404        QuartzShowFullscreen(TRUE);
405}
406
407
408/*
409 * QuartzHide
410 *  Remove the X server display from the screen. Does nothing if already
411 *  hidden. Calls mode specific screen suspend to set X clip regions to
412 *  prevent drawing (if needed) and restore the Aqua cursor.
413 */
414void QuartzHide(void)
415{
416    int i;
417
418    if (XQuartzServerVisible) {
419        for (i = 0; i < screenInfo.numScreens; i++) {
420            if (screenInfo.screens[i]) {
421                quartzProcs->SuspendScreen(screenInfo.screens[i]);
422            }
423        }
424    }
425
426    if(!XQuartzIsRootless)
427        QuartzShowFullscreen(FALSE);
428    XQuartzServerVisible = FALSE;
429}
430
431
432/*
433 * QuartzSetRootClip
434 *  Enable or disable rendering to the X screen.
435 */
436void QuartzSetRootClip(
437    BOOL enable)
438{
439    int i;
440
441    if (!XQuartzServerVisible)
442        return;
443
444    for (i = 0; i < screenInfo.numScreens; i++) {
445        if (screenInfo.screens[i]) {
446            xf86SetRootClip(screenInfo.screens[i], enable);
447        }
448    }
449}
450
451/*
452 * QuartzSpaceChanged
453 *  Unmap offscreen windows, map onscreen windows
454 */
455void QuartzSpaceChanged(uint32_t space_id) {
456    /* Do something special here, so we don't depend on quartz-wm for spaces to work... */
457    DEBUG_LOG("Space Changed (%u) ... do something interesting...\n", space_id);
458}
459
460/*
461 * QuartzCopyDisplayIDs
462 *  Associate an X11 screen with one or more CoreGraphics display IDs by copying
463 *  the list into a private array. Free the previously copied array, if present.
464 */
465void QuartzCopyDisplayIDs(ScreenPtr pScreen,
466                          int displayCount, CGDirectDisplayID *displayIDs) {
467    QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
468
469    free(pQuartzScreen->displayIDs);
470    if(displayCount) {
471        size_t size = displayCount * sizeof(CGDirectDisplayID);
472        pQuartzScreen->displayIDs = malloc(size);
473        memcpy(pQuartzScreen->displayIDs, displayIDs, size);
474    } else {
475       pQuartzScreen->displayIDs = NULL;
476    }
477    pQuartzScreen->displayCount = displayCount;
478}
479
480void NSBeep(void);
481void DDXRingBell(
482    int volume,         // volume is % of max
483    int pitch,          // pitch is Hz
484    int duration)       // duration is milliseconds
485{
486    if (volume)
487        NSBeep();
488}
489