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