X11Application.m revision 1b5d61b8
14642e01fSmrg/* X11Application.m -- subclass of NSApplication to multiplex events
235c4bbdfSmrg *
335c4bbdfSmrg * Copyright (c) 2002-2012 Apple Inc. All rights reserved.
435c4bbdfSmrg *
535c4bbdfSmrg * Permission is hereby granted, free of charge, to any person
635c4bbdfSmrg * obtaining a copy of this software and associated documentation files
735c4bbdfSmrg * (the "Software"), to deal in the Software without restriction,
835c4bbdfSmrg * including without limitation the rights to use, copy, modify, merge,
935c4bbdfSmrg * publish, distribute, sublicense, and/or sell copies of the Software,
1035c4bbdfSmrg * and to permit persons to whom the Software is furnished to do so,
1135c4bbdfSmrg * subject to the following conditions:
1235c4bbdfSmrg *
1335c4bbdfSmrg * The above copyright notice and this permission notice shall be
1435c4bbdfSmrg * included in all copies or substantial portions of the Software.
1535c4bbdfSmrg *
1635c4bbdfSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1735c4bbdfSmrg * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1835c4bbdfSmrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
1935c4bbdfSmrg * NONINFRINGEMENT.  IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
2035c4bbdfSmrg * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
2135c4bbdfSmrg * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2235c4bbdfSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2335c4bbdfSmrg * DEALINGS IN THE SOFTWARE.
2435c4bbdfSmrg *
2535c4bbdfSmrg * Except as contained in this notice, the name(s) of the above
2635c4bbdfSmrg * copyright holders shall not be used in advertising or otherwise to
2735c4bbdfSmrg * promote the sale, use or other dealings in this Software without
2835c4bbdfSmrg * prior written authorization.
2935c4bbdfSmrg */
304642e01fSmrg
314642e01fSmrg#include "sanitizedCarbon.h"
324642e01fSmrg
334642e01fSmrg#ifdef HAVE_DIX_CONFIG_H
344642e01fSmrg#include <dix-config.h>
354642e01fSmrg#endif
364642e01fSmrg
374642e01fSmrg#include "quartzCommon.h"
384642e01fSmrg
394642e01fSmrg#import "X11Application.h"
404642e01fSmrg
414642e01fSmrg#include "darwin.h"
426747b715Smrg#include "quartz.h"
434642e01fSmrg#include "darwinEvents.h"
444642e01fSmrg#include "quartzKeyboard.h"
456747b715Smrg#include <X11/extensions/applewmconst.h>
464642e01fSmrg#include "micmap.h"
476747b715Smrg#include "exglobals.h"
484642e01fSmrg
494642e01fSmrg#include <mach/mach.h>
504642e01fSmrg#include <unistd.h>
514642e01fSmrg#include <AvailabilityMacros.h>
524642e01fSmrg
539ace9065Smrg#include <pthread.h>
549ace9065Smrg
554642e01fSmrg#include <Xplugin.h>
564642e01fSmrg
574642e01fSmrg// pbproxy/pbproxy.h
5835c4bbdfSmrgextern int
5935c4bbdfSmrgxpbproxy_run(void);
604642e01fSmrg
6135c4bbdfSmrg#define DEFAULTS_FILE X11LIBDIR "/X11/xserver/Xquartz.plist"
624642e01fSmrg
634642e01fSmrg#ifndef XSERVER_VERSION
644642e01fSmrg#define XSERVER_VERSION "?"
654642e01fSmrg#endif
664642e01fSmrg
6735c4bbdfSmrg#ifdef HAVE_LIBDISPATCH
6835c4bbdfSmrg#include <dispatch/dispatch.h>
6935c4bbdfSmrg
7035c4bbdfSmrgstatic dispatch_queue_t eventTranslationQueue;
7135c4bbdfSmrg#endif
7235c4bbdfSmrg
7335c4bbdfSmrg#ifndef __has_feature
7435c4bbdfSmrg#define __has_feature(x) 0
7535c4bbdfSmrg#endif
7635c4bbdfSmrg
7735c4bbdfSmrg#ifndef CF_RETURNS_RETAINED
7835c4bbdfSmrg#if __has_feature(attribute_cf_returns_retained)
7935c4bbdfSmrg#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
8035c4bbdfSmrg#else
8135c4bbdfSmrg#define CF_RETURNS_RETAINED
8235c4bbdfSmrg#endif
8335c4bbdfSmrg#endif
844642e01fSmrg
854642e01fSmrgextern Bool noTestExtensions;
8635c4bbdfSmrgextern Bool noRenderExtension;
874642e01fSmrg
884642e01fSmrg#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
894642e01fSmrgstatic TISInputSourceRef last_key_layout;
904642e01fSmrg#else
914642e01fSmrgstatic KeyboardLayoutRef last_key_layout;
924642e01fSmrg#endif
934642e01fSmrg
94475c125cSmrg/* This preference is only tested on Lion or later as it's not relevant to
95475c125cSmrg * earlier OS versions.
96475c125cSmrg */
97475c125cSmrgBool XQuartzScrollInDeviceDirection = FALSE;
98475c125cSmrg
994642e01fSmrgextern int darwinFakeButtons;
1004642e01fSmrg
1016747b715Smrg/* Store the mouse location while in the background, and update X11's pointer
1026747b715Smrg * location when we become the foreground application
1036747b715Smrg */
1046747b715Smrgstatic NSPoint bgMouseLocation;
1056747b715Smrgstatic BOOL bgMouseLocationUpdated = FALSE;
1066747b715Smrg
1074642e01fSmrgX11Application *X11App;
1084642e01fSmrg
1094642e01fSmrgCFStringRef app_prefs_domain_cfstr = NULL;
1104642e01fSmrg
11135c4bbdfSmrg#define ALL_KEY_MASKS (NSShiftKeyMask | NSControlKeyMask | \
11235c4bbdfSmrg                       NSAlternateKeyMask | NSCommandKeyMask)
1134642e01fSmrg
1144642e01fSmrg@interface X11Application (Private)
1154642e01fSmrg- (void) sendX11NSEvent:(NSEvent *)e;
1164642e01fSmrg@end
1174642e01fSmrg
1184642e01fSmrg@implementation X11Application
1194642e01fSmrg
1204642e01fSmrgtypedef struct message_struct message;
1214642e01fSmrgstruct message_struct {
1224642e01fSmrg    mach_msg_header_t hdr;
1234642e01fSmrg    SEL selector;
1244642e01fSmrg    NSObject *arg;
1254642e01fSmrg};
1264642e01fSmrg
1274642e01fSmrgstatic mach_port_t _port;
1284642e01fSmrg
1294642e01fSmrg/* Quartz mode initialization routine. This is often dynamically loaded
1304642e01fSmrg   but is statically linked into this X server. */
13135c4bbdfSmrgBool
13235c4bbdfSmrgQuartzModeBundleInit(void);
1334642e01fSmrg
13435c4bbdfSmrgstatic void
13535c4bbdfSmrginit_ports(void)
13635c4bbdfSmrg{
1374642e01fSmrg    kern_return_t r;
1384642e01fSmrg    NSPort *p;
13935c4bbdfSmrg
1404642e01fSmrg    if (_port != MACH_PORT_NULL) return;
14135c4bbdfSmrg
14235c4bbdfSmrg    r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &_port);
1434642e01fSmrg    if (r != KERN_SUCCESS) return;
14435c4bbdfSmrg
1454642e01fSmrg    p = [NSMachPort portWithMachPort:_port];
1464642e01fSmrg    [p setDelegate:NSApp];
14735c4bbdfSmrg    [p scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:
14835c4bbdfSmrg     NSDefaultRunLoopMode];
1494642e01fSmrg}
1504642e01fSmrg
15135c4bbdfSmrgstatic void
15235c4bbdfSmrgmessage_kit_thread(SEL selector, NSObject *arg)
15335c4bbdfSmrg{
1544642e01fSmrg    message msg;
1554642e01fSmrg    kern_return_t r;
15635c4bbdfSmrg
15735c4bbdfSmrg    msg.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
15835c4bbdfSmrg    msg.hdr.msgh_size = sizeof(msg);
1594642e01fSmrg    msg.hdr.msgh_remote_port = _port;
1604642e01fSmrg    msg.hdr.msgh_local_port = MACH_PORT_NULL;
1614642e01fSmrg    msg.hdr.msgh_reserved = 0;
1624642e01fSmrg    msg.hdr.msgh_id = 0;
16335c4bbdfSmrg
1644642e01fSmrg    msg.selector = selector;
1654642e01fSmrg    msg.arg = [arg retain];
16635c4bbdfSmrg
16735c4bbdfSmrg    r = mach_msg(&msg.hdr, MACH_SEND_MSG, msg.hdr.msgh_size,
16835c4bbdfSmrg                 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
1694642e01fSmrg    if (r != KERN_SUCCESS)
17035c4bbdfSmrg        ErrorF("%s: mach_msg failed: %x\n", __FUNCTION__, r);
1714642e01fSmrg}
1724642e01fSmrg
17335c4bbdfSmrg- (void) handleMachMessage:(void *)_msg
17435c4bbdfSmrg{
1754642e01fSmrg    message *msg = _msg;
17635c4bbdfSmrg
1774642e01fSmrg    [self performSelector:msg->selector withObject:msg->arg];
1784642e01fSmrg    [msg->arg release];
1794642e01fSmrg}
1804642e01fSmrg
18135c4bbdfSmrg- (void) set_controller:obj
18235c4bbdfSmrg{
1834642e01fSmrg    if (_controller == nil) _controller = [obj retain];
1844642e01fSmrg}
1854642e01fSmrg
18635c4bbdfSmrg- (void) dealloc
18735c4bbdfSmrg{
1884642e01fSmrg    if (_controller != nil) [_controller release];
18935c4bbdfSmrg
1904642e01fSmrg    if (_port != MACH_PORT_NULL)
19135c4bbdfSmrg        mach_port_deallocate(mach_task_self(), _port);
19235c4bbdfSmrg
1934642e01fSmrg    [super dealloc];
1944642e01fSmrg}
1954642e01fSmrg
19635c4bbdfSmrg- (void) orderFrontStandardAboutPanel: (id) sender
19735c4bbdfSmrg{
1984642e01fSmrg    NSMutableDictionary *dict;
1994642e01fSmrg    NSDictionary *infoDict;
2004642e01fSmrg    NSString *tem;
20135c4bbdfSmrg
2024642e01fSmrg    dict = [NSMutableDictionary dictionaryWithCapacity:3];
2034642e01fSmrg    infoDict = [[NSBundle mainBundle] infoDictionary];
20435c4bbdfSmrg
20535c4bbdfSmrg    [dict setObject: NSLocalizedString(@"The X Window System", @"About panel")
20635c4bbdfSmrg             forKey:@"ApplicationName"];
20735c4bbdfSmrg
2084642e01fSmrg    tem = [infoDict objectForKey:@"CFBundleShortVersionString"];
20935c4bbdfSmrg
2104642e01fSmrg    [dict setObject:[NSString stringWithFormat:@"XQuartz %@", tem]
21135c4bbdfSmrg             forKey:@"ApplicationVersion"];
21235c4bbdfSmrg
21335c4bbdfSmrg    [dict setObject:[NSString stringWithFormat:@"xorg-server %s",
21435c4bbdfSmrg                     XSERVER_VERSION]
21535c4bbdfSmrg     forKey:@"Version"];
2164642e01fSmrg
2174642e01fSmrg    [self orderFrontStandardAboutPanelWithOptions: dict];
2184642e01fSmrg}
2194642e01fSmrg
22035c4bbdfSmrg- (void) activateX:(OSX_BOOL)state
22135c4bbdfSmrg{
22235c4bbdfSmrg    if (_x_active == state)
22335c4bbdfSmrg        return;
22435c4bbdfSmrg
22535c4bbdfSmrg    DEBUG_LOG("state=%d, _x_active=%d, \n", state, _x_active);
2264642e01fSmrg    if (state) {
22735c4bbdfSmrg        if (bgMouseLocationUpdated) {
22835c4bbdfSmrg            DarwinSendPointerEvents(darwinPointer, MotionNotify, 0,
22935c4bbdfSmrg                                    bgMouseLocation.x, bgMouseLocation.y,
23035c4bbdfSmrg                                    0.0, 0.0);
2316747b715Smrg            bgMouseLocationUpdated = FALSE;
2324642e01fSmrg        }
2336747b715Smrg        DarwinSendDDXEvent(kXquartzActivate, 0);
23435c4bbdfSmrg    }
23535c4bbdfSmrg    else {
2364642e01fSmrg
23735c4bbdfSmrg        if (darwin_all_modifier_flags)
2384642e01fSmrg            DarwinUpdateModKeys(0);
23935c4bbdfSmrg
24035c4bbdfSmrg        DarwinInputReleaseButtonsAndKeys(darwinKeyboard);
24135c4bbdfSmrg        DarwinInputReleaseButtonsAndKeys(darwinPointer);
24235c4bbdfSmrg        DarwinInputReleaseButtonsAndKeys(darwinTabletCursor);
24335c4bbdfSmrg        DarwinInputReleaseButtonsAndKeys(darwinTabletStylus);
24435c4bbdfSmrg        DarwinInputReleaseButtonsAndKeys(darwinTabletEraser);
24535c4bbdfSmrg
2464642e01fSmrg        DarwinSendDDXEvent(kXquartzDeactivate, 0);
2474642e01fSmrg    }
2484642e01fSmrg
2494642e01fSmrg    _x_active = state;
2504642e01fSmrg}
2514642e01fSmrg
25235c4bbdfSmrg- (void) became_key:(NSWindow *)win
25335c4bbdfSmrg{
25435c4bbdfSmrg    [self activateX:NO];
2554642e01fSmrg}
2564642e01fSmrg
25735c4bbdfSmrg- (void) sendEvent:(NSEvent *)e
25835c4bbdfSmrg{
2594642e01fSmrg    OSX_BOOL for_appkit, for_x;
26035c4bbdfSmrg
2614642e01fSmrg    /* By default pass down the responder chain and to X. */
2624642e01fSmrg    for_appkit = YES;
2634642e01fSmrg    for_x = YES;
26435c4bbdfSmrg
2654642e01fSmrg    switch ([e type]) {
26635c4bbdfSmrg    case NSLeftMouseDown:
26735c4bbdfSmrg    case NSRightMouseDown:
26835c4bbdfSmrg    case NSOtherMouseDown:
26935c4bbdfSmrg    case NSLeftMouseUp:
27035c4bbdfSmrg    case NSRightMouseUp:
27135c4bbdfSmrg    case NSOtherMouseUp:
27235c4bbdfSmrg        if ([e window] != nil) {
27335c4bbdfSmrg            /* Pointer event has an (AppKit) window. Probably something for the kit. */
27435c4bbdfSmrg            for_x = NO;
27535c4bbdfSmrg            if (_x_active) [self activateX:NO];
27635c4bbdfSmrg        }
27735c4bbdfSmrg        else if ([self modalWindow] == nil) {
2781b5d61b8Smrg            /* Must be an X window. Tell appkit windows to resign main/key */
27935c4bbdfSmrg            for_appkit = NO;
28035c4bbdfSmrg
2811b5d61b8Smrg            if (!_x_active && quartzProcs->IsX11Window([e windowNumber])) {
2821b5d61b8Smrg                if ([self respondsToSelector:@selector(_setKeyWindow:)] && [self respondsToSelector:@selector(_setMainWindow:)]) {
2831b5d61b8Smrg                    NSWindow *keyWindow = [self keyWindow];
2841b5d61b8Smrg                    if (keyWindow) {
2851b5d61b8Smrg                        [self _setKeyWindow:nil];
2861b5d61b8Smrg                        [keyWindow resignKeyWindow];
2871b5d61b8Smrg                    }
2881b5d61b8Smrg
2891b5d61b8Smrg                    NSWindow *mainWindow = [self mainWindow];
2901b5d61b8Smrg                    if (mainWindow) {
2911b5d61b8Smrg                        [self _setMainWindow:nil];
2921b5d61b8Smrg                        [mainWindow resignMainWindow];
2931b5d61b8Smrg                   }
2941b5d61b8Smrg                 } else {
2951b5d61b8Smrg                    /* This has a side effect of causing background apps to steal focus from XQuartz.
2961b5d61b8Smrg                     * Unfortunately, there is no public and stable API to do what we want, but this
2971b5d61b8Smrg                     * is a decent fallback in the off chance that the above selectors get dropped
2981b5d61b8Smrg                     * in the future.
2991b5d61b8Smrg                     */
3001b5d61b8Smrg                    [self deactivate];
3011b5d61b8Smrg                }
3021b5d61b8Smrg
3031b5d61b8Smrg                [self activateX:YES];
3044642e01fSmrg            }
30535c4bbdfSmrg        }
3064642e01fSmrg
30735c4bbdfSmrg        /* We want to force sending to appkit if we're over the menu bar */
30835c4bbdfSmrg        if (!for_appkit) {
30935c4bbdfSmrg            NSPoint NSlocation = [e locationInWindow];
31035c4bbdfSmrg            NSWindow *window = [e window];
31135c4bbdfSmrg            NSRect NSframe, NSvisibleFrame;
31235c4bbdfSmrg            CGRect CGframe, CGvisibleFrame;
31335c4bbdfSmrg            CGPoint CGlocation;
31435c4bbdfSmrg
31535c4bbdfSmrg            if (window != nil) {
31635c4bbdfSmrg                NSRect frame = [window frame];
31735c4bbdfSmrg                NSlocation.x += frame.origin.x;
31835c4bbdfSmrg                NSlocation.y += frame.origin.y;
3194642e01fSmrg            }
32035c4bbdfSmrg
32135c4bbdfSmrg            NSframe = [[NSScreen mainScreen] frame];
32235c4bbdfSmrg            NSvisibleFrame = [[NSScreen mainScreen] visibleFrame];
32335c4bbdfSmrg
32435c4bbdfSmrg            CGframe = CGRectMake(NSframe.origin.x, NSframe.origin.y,
32535c4bbdfSmrg                                 NSframe.size.width, NSframe.size.height);
32635c4bbdfSmrg            CGvisibleFrame = CGRectMake(NSvisibleFrame.origin.x,
32735c4bbdfSmrg                                        NSvisibleFrame.origin.y,
32835c4bbdfSmrg                                        NSvisibleFrame.size.width,
32935c4bbdfSmrg                                        NSvisibleFrame.size.height);
33035c4bbdfSmrg            CGlocation = CGPointMake(NSlocation.x, NSlocation.y);
33135c4bbdfSmrg
33235c4bbdfSmrg            if (CGRectContainsPoint(CGframe, CGlocation) &&
33335c4bbdfSmrg                !CGRectContainsPoint(CGvisibleFrame, CGlocation))
33435c4bbdfSmrg                for_appkit = YES;
33535c4bbdfSmrg        }
33635c4bbdfSmrg
33735c4bbdfSmrg        break;
33835c4bbdfSmrg
33935c4bbdfSmrg    case NSKeyDown:
34035c4bbdfSmrg    case NSKeyUp:
34135c4bbdfSmrg
34235c4bbdfSmrg        if (_x_active) {
34335c4bbdfSmrg            static BOOL do_swallow = NO;
34435c4bbdfSmrg            static int swallow_keycode;
34535c4bbdfSmrg
34635c4bbdfSmrg            if ([e type] == NSKeyDown) {
34735c4bbdfSmrg                /* Before that though, see if there are any global
34835c4bbdfSmrg                 * shortcuts bound to it. */
34935c4bbdfSmrg
35035c4bbdfSmrg                if (darwinAppKitModMask &[e modifierFlags]) {
35135c4bbdfSmrg                    /* Override to force sending to Appkit */
35235c4bbdfSmrg                    swallow_keycode = [e keyCode];
35335c4bbdfSmrg                    do_swallow = YES;
35435c4bbdfSmrg                    for_x = NO;
3554642e01fSmrg#if XPLUGIN_VERSION >= 1
35635c4bbdfSmrg                }
35735c4bbdfSmrg                else if (XQuartzEnableKeyEquivalents &&
35835c4bbdfSmrg                         xp_is_symbolic_hotkey_event([e eventRef])) {
35935c4bbdfSmrg                    swallow_keycode = [e keyCode];
36035c4bbdfSmrg                    do_swallow = YES;
36135c4bbdfSmrg                    for_x = NO;
3624642e01fSmrg#endif
3634642e01fSmrg                }
36435c4bbdfSmrg                else if (XQuartzEnableKeyEquivalents &&
36535c4bbdfSmrg                         [[self mainMenu] performKeyEquivalent:e]) {
36635c4bbdfSmrg                    swallow_keycode = [e keyCode];
36735c4bbdfSmrg                    do_swallow = YES;
36835c4bbdfSmrg                    for_appkit = NO;
36935c4bbdfSmrg                    for_x = NO;
37035c4bbdfSmrg                }
37135c4bbdfSmrg                else if (!XQuartzIsRootless
37235c4bbdfSmrg                         && ([e modifierFlags] & ALL_KEY_MASKS) ==
37335c4bbdfSmrg                         (NSCommandKeyMask | NSAlternateKeyMask)
37435c4bbdfSmrg                         && ([e keyCode] == 0 /*a*/ || [e keyCode] ==
37535c4bbdfSmrg                             53 /*Esc*/)) {
37635c4bbdfSmrg                    /* We have this here to force processing fullscreen
37735c4bbdfSmrg                     * toggle even if XQuartzEnableKeyEquivalents is disabled */
37835c4bbdfSmrg                    swallow_keycode = [e keyCode];
37935c4bbdfSmrg                    do_swallow = YES;
38035c4bbdfSmrg                    for_x = NO;
38135c4bbdfSmrg                    for_appkit = NO;
38235c4bbdfSmrg                    DarwinSendDDXEvent(kXquartzToggleFullscreen, 0);
38335c4bbdfSmrg                }
38435c4bbdfSmrg                else {
38535c4bbdfSmrg                    /* No kit window is focused, so send it to X. */
38635c4bbdfSmrg                    for_appkit = NO;
3871b5d61b8Smrg
3881b5d61b8Smrg                    /* Reset our swallow state if we're seeing the same keyCode again.
3891b5d61b8Smrg                     * This can happen if we become !_x_active when the keyCode we
3901b5d61b8Smrg                     * intended to swallow is delivered.  See:
3911b5d61b8Smrg                     * https://bugs.freedesktop.org/show_bug.cgi?id=92648
3921b5d61b8Smrg                     */
3931b5d61b8Smrg                    if ([e keyCode] == swallow_keycode) {
3941b5d61b8Smrg                        do_swallow = NO;
3951b5d61b8Smrg                    }
39635c4bbdfSmrg                }
39735c4bbdfSmrg            }
39835c4bbdfSmrg            else {       /* KeyUp */
39935c4bbdfSmrg                /* If we saw a key equivalent on the down, don't pass
40035c4bbdfSmrg                 * the up through to X. */
40135c4bbdfSmrg                if (do_swallow && [e keyCode] == swallow_keycode) {
40235c4bbdfSmrg                    do_swallow = NO;
40335c4bbdfSmrg                    for_x = NO;
40435c4bbdfSmrg                }
40535c4bbdfSmrg            }
40635c4bbdfSmrg        }
40735c4bbdfSmrg        else {       /* !_x_active */
40835c4bbdfSmrg            for_x = NO;
40935c4bbdfSmrg        }
41035c4bbdfSmrg        break;
41135c4bbdfSmrg
41235c4bbdfSmrg    case NSFlagsChanged:
41335c4bbdfSmrg        /* Don't tell X11 about modifiers changing while it's not active */
41435c4bbdfSmrg        if (!_x_active)
41535c4bbdfSmrg            for_x = NO;
41635c4bbdfSmrg        break;
41735c4bbdfSmrg
41835c4bbdfSmrg    case NSAppKitDefined:
41935c4bbdfSmrg        switch ([e subtype]) {
42035c4bbdfSmrg            static BOOL x_was_active = NO;
42135c4bbdfSmrg
42235c4bbdfSmrg        case NSApplicationActivatedEventType:
42335c4bbdfSmrg            for_x = NO;
42435c4bbdfSmrg            if ([e window] == nil && x_was_active) {
42535c4bbdfSmrg                BOOL order_all_windows = YES, workspaces, ok;
42635c4bbdfSmrg                for_appkit = NO;
42735c4bbdfSmrg
42835c4bbdfSmrg                /* FIXME: This is a hack to avoid passing the event to AppKit which
42935c4bbdfSmrg                 *        would result in it raising one of its windows.
43035c4bbdfSmrg                 */
43135c4bbdfSmrg                _appFlags._active = YES;
43235c4bbdfSmrg
43335c4bbdfSmrg                [self set_front_process:nil];
43435c4bbdfSmrg
43535c4bbdfSmrg                /* Get the Spaces preference for SwitchOnActivate */
43635c4bbdfSmrg                (void)CFPreferencesAppSynchronize(CFSTR("com.apple.dock"));
43735c4bbdfSmrg                workspaces =
43835c4bbdfSmrg                    CFPreferencesGetAppBooleanValue(CFSTR("workspaces"),
43935c4bbdfSmrg                                                    CFSTR(
44035c4bbdfSmrg                                                        "com.apple.dock"),
44135c4bbdfSmrg                                                    &ok);
44235c4bbdfSmrg                if (!ok)
44335c4bbdfSmrg                    workspaces = NO;
44435c4bbdfSmrg
44535c4bbdfSmrg                if (workspaces) {
44635c4bbdfSmrg                    (void)CFPreferencesAppSynchronize(CFSTR(
44735c4bbdfSmrg                                                          ".GlobalPreferences"));
44835c4bbdfSmrg                    order_all_windows =
44935c4bbdfSmrg                        CFPreferencesGetAppBooleanValue(CFSTR(
45035c4bbdfSmrg                                                            "AppleSpacesSwitchOnActivate"),
45135c4bbdfSmrg                                                        CFSTR(
45235c4bbdfSmrg                                                            ".GlobalPreferences"),
45335c4bbdfSmrg                                                        &ok);
45435c4bbdfSmrg                    if (!ok)
45535c4bbdfSmrg                        order_all_windows = YES;
45635c4bbdfSmrg                }
45735c4bbdfSmrg
45835c4bbdfSmrg                /* TODO: In the workspaces && !AppleSpacesSwitchOnActivate case, the windows are ordered
45935c4bbdfSmrg                 *       correctly, but we need to activate the top window on this space if there is
46035c4bbdfSmrg                 *       none active.
46135c4bbdfSmrg                 *
46235c4bbdfSmrg                 *       If there are no active windows, and there are minimized windows, we should
46335c4bbdfSmrg                 *       be restoring one of them.
46435c4bbdfSmrg                 */
46535c4bbdfSmrg                if ([e data2] & 0x10) {         // 0x10 (bfCPSOrderAllWindowsForward) is set when we use cmd-tab or the dock icon
46635c4bbdfSmrg                    DarwinSendDDXEvent(kXquartzBringAllToFront, 1,
46735c4bbdfSmrg                                       order_all_windows);
46835c4bbdfSmrg                }
4694642e01fSmrg            }
4704642e01fSmrg            break;
47135c4bbdfSmrg
47235c4bbdfSmrg        case 18:         /* ApplicationDidReactivate */
47335c4bbdfSmrg            if (XQuartzFullscreenVisible) for_appkit = NO;
4744642e01fSmrg            break;
4759ace9065Smrg
47635c4bbdfSmrg        case NSApplicationDeactivatedEventType:
47735c4bbdfSmrg            for_x = NO;
4789ace9065Smrg
47935c4bbdfSmrg            x_was_active = _x_active;
48035c4bbdfSmrg            if (_x_active)
48135c4bbdfSmrg                [self activateX:NO];
4824642e01fSmrg            break;
48335c4bbdfSmrg        }
48435c4bbdfSmrg        break;
48535c4bbdfSmrg
48635c4bbdfSmrg    default:
48735c4bbdfSmrg        break;          /* for gcc */
4884642e01fSmrg    }
48935c4bbdfSmrg
4904642e01fSmrg    if (for_appkit) [super sendEvent:e];
49135c4bbdfSmrg
49235c4bbdfSmrg    if (for_x) {
49335c4bbdfSmrg#ifdef HAVE_LIBDISPATCH
49435c4bbdfSmrg        dispatch_async(eventTranslationQueue, ^{
49535c4bbdfSmrg                           [self sendX11NSEvent:e];
49635c4bbdfSmrg                       });
49735c4bbdfSmrg#else
49835c4bbdfSmrg        [self sendX11NSEvent:e];
49935c4bbdfSmrg#endif
50035c4bbdfSmrg    }
5014642e01fSmrg}
5024642e01fSmrg
50335c4bbdfSmrg- (void) set_window_menu:(NSArray *)list
50435c4bbdfSmrg{
50535c4bbdfSmrg    [_controller set_window_menu:list];
5064642e01fSmrg}
5074642e01fSmrg
50835c4bbdfSmrg- (void) set_window_menu_check:(NSNumber *)n
50935c4bbdfSmrg{
51035c4bbdfSmrg    [_controller set_window_menu_check:n];
5114642e01fSmrg}
5124642e01fSmrg
51335c4bbdfSmrg- (void) set_apps_menu:(NSArray *)list
51435c4bbdfSmrg{
51535c4bbdfSmrg    [_controller set_apps_menu:list];
5164642e01fSmrg}
5174642e01fSmrg
51835c4bbdfSmrg- (void) set_front_process:unused
51935c4bbdfSmrg{
52035c4bbdfSmrg    [NSApp activateIgnoringOtherApps:YES];
5214642e01fSmrg
52235c4bbdfSmrg    if ([self modalWindow] == nil)
52335c4bbdfSmrg        [self activateX:YES];
5244642e01fSmrg}
5254642e01fSmrg
52635c4bbdfSmrg- (void) set_can_quit:(NSNumber *)state
52735c4bbdfSmrg{
52835c4bbdfSmrg    [_controller set_can_quit:[state boolValue]];
5294642e01fSmrg}
5304642e01fSmrg
53135c4bbdfSmrg- (void) server_ready:unused
53235c4bbdfSmrg{
53335c4bbdfSmrg    [_controller server_ready];
5344642e01fSmrg}
5354642e01fSmrg
53635c4bbdfSmrg- (void) show_hide_menubar:(NSNumber *)state
53735c4bbdfSmrg{
5384642e01fSmrg    /* Also shows/hides the dock */
5394642e01fSmrg    if ([state boolValue])
54035c4bbdfSmrg        SetSystemUIMode(kUIModeNormal, 0);
5414642e01fSmrg    else
54235c4bbdfSmrg        SetSystemUIMode(kUIModeAllHidden,
54335c4bbdfSmrg                        XQuartzFullscreenMenu ? kUIOptionAutoShowMenuBar : 0);                   // kUIModeAllSuppressed or kUIOptionAutoShowMenuBar can be used to allow "mouse-activation"
5444642e01fSmrg}
5454642e01fSmrg
54635c4bbdfSmrg- (void) launch_client:(NSString *)cmd
54735c4bbdfSmrg{
5486747b715Smrg    (void)[_controller application:self openFile:cmd];
5496747b715Smrg}
5504642e01fSmrg
5514642e01fSmrg/* user preferences */
5524642e01fSmrg
5534642e01fSmrg/* Note that these functions only work for arrays whose elements
55435c4bbdfSmrg   can be toll-free-bridged between NS and CF worlds. */
5554642e01fSmrg
55635c4bbdfSmrgstatic const void *
55735c4bbdfSmrgcfretain(CFAllocatorRef a, const void *b)
55835c4bbdfSmrg{
55935c4bbdfSmrg    return CFRetain(b);
5604642e01fSmrg}
5614642e01fSmrg
56235c4bbdfSmrgstatic void
56335c4bbdfSmrgcfrelease(CFAllocatorRef a, const void *b)
56435c4bbdfSmrg{
56535c4bbdfSmrg    CFRelease(b);
5664642e01fSmrg}
5674642e01fSmrg
56835c4bbdfSmrgCF_RETURNS_RETAINED
56935c4bbdfSmrgstatic CFMutableArrayRef
57035c4bbdfSmrgnsarray_to_cfarray(NSArray *in)
57135c4bbdfSmrg{
57235c4bbdfSmrg    CFMutableArrayRef out;
57335c4bbdfSmrg    CFArrayCallBacks cb;
57435c4bbdfSmrg    NSObject *ns;
57535c4bbdfSmrg    const CFTypeRef *cf;
57635c4bbdfSmrg    int i, count;
57735c4bbdfSmrg
57835c4bbdfSmrg    memset(&cb, 0, sizeof(cb));
57935c4bbdfSmrg    cb.version = 0;
58035c4bbdfSmrg    cb.retain = cfretain;
58135c4bbdfSmrg    cb.release = cfrelease;
58235c4bbdfSmrg
58335c4bbdfSmrg    count = [in count];
58435c4bbdfSmrg    out = CFArrayCreateMutable(NULL, count, &cb);
58535c4bbdfSmrg
58635c4bbdfSmrg    for (i = 0; i < count; i++) {
58735c4bbdfSmrg        ns = [in objectAtIndex:i];
58835c4bbdfSmrg
58935c4bbdfSmrg        if ([ns isKindOfClass:[NSArray class]])
59035c4bbdfSmrg            cf = (CFTypeRef)nsarray_to_cfarray((NSArray *)ns);
59135c4bbdfSmrg        else
59235c4bbdfSmrg            cf = CFRetain((CFTypeRef)ns);
59335c4bbdfSmrg
59435c4bbdfSmrg        CFArrayAppendValue(out, cf);
59535c4bbdfSmrg        CFRelease(cf);
59635c4bbdfSmrg    }
59735c4bbdfSmrg
59835c4bbdfSmrg    return out;
59935c4bbdfSmrg}
6004642e01fSmrg
60135c4bbdfSmrgstatic NSMutableArray *
60235c4bbdfSmrgcfarray_to_nsarray(CFArrayRef in)
60335c4bbdfSmrg{
60435c4bbdfSmrg    NSMutableArray *out;
60535c4bbdfSmrg    const CFTypeRef *cf;
60635c4bbdfSmrg    NSObject *ns;
60735c4bbdfSmrg    int i, count;
6084642e01fSmrg
60935c4bbdfSmrg    count = CFArrayGetCount(in);
61035c4bbdfSmrg    out = [[NSMutableArray alloc] initWithCapacity:count];
6114642e01fSmrg
61235c4bbdfSmrg    for (i = 0; i < count; i++) {
61335c4bbdfSmrg        cf = CFArrayGetValueAtIndex(in, i);
6144642e01fSmrg
61535c4bbdfSmrg        if (CFGetTypeID(cf) == CFArrayGetTypeID())
61635c4bbdfSmrg            ns = cfarray_to_nsarray((CFArrayRef)cf);
61735c4bbdfSmrg        else
61835c4bbdfSmrg            ns = [(id) cf retain];
6194642e01fSmrg
62035c4bbdfSmrg        [out addObject:ns];
62135c4bbdfSmrg        [ns release];
62235c4bbdfSmrg    }
6234642e01fSmrg
62435c4bbdfSmrg    return out;
6254642e01fSmrg}
6264642e01fSmrg
62735c4bbdfSmrg- (CFPropertyListRef) prefs_get_copy:(NSString *)key
62835c4bbdfSmrg{
62935c4bbdfSmrg    CFPropertyListRef value;
63035c4bbdfSmrg
63135c4bbdfSmrg    value = CFPreferencesCopyAppValue((CFStringRef)key,
63235c4bbdfSmrg                                      app_prefs_domain_cfstr);
63335c4bbdfSmrg
63435c4bbdfSmrg    if (value == NULL) {
63535c4bbdfSmrg        static CFDictionaryRef defaults;
63635c4bbdfSmrg
63735c4bbdfSmrg        if (defaults == NULL) {
63835c4bbdfSmrg            CFStringRef error = NULL;
63935c4bbdfSmrg            CFDataRef data;
64035c4bbdfSmrg            CFURLRef url;
64135c4bbdfSmrg            SInt32 error_code;
64235c4bbdfSmrg
64335c4bbdfSmrg            url = (CFURLCreateFromFileSystemRepresentation
64435c4bbdfSmrg                       (NULL, (unsigned char *)DEFAULTS_FILE,
64535c4bbdfSmrg                       strlen(DEFAULTS_FILE), false));
64635c4bbdfSmrg            if (CFURLCreateDataAndPropertiesFromResource(NULL, url, &data,
64735c4bbdfSmrg                                                         NULL, NULL,
64835c4bbdfSmrg                                                         &error_code)) {
64935c4bbdfSmrg                defaults = (CFPropertyListCreateFromXMLData
65035c4bbdfSmrg                                (NULL, data,
65135c4bbdfSmrg                                kCFPropertyListMutableContainersAndLeaves,
65235c4bbdfSmrg                                &error));
65335c4bbdfSmrg                if (error != NULL) CFRelease(error);
65435c4bbdfSmrg                CFRelease(data);
65535c4bbdfSmrg            }
65635c4bbdfSmrg            CFRelease(url);
65735c4bbdfSmrg
65835c4bbdfSmrg            if (defaults != NULL) {
65935c4bbdfSmrg                NSMutableArray *apps, *elt;
66035c4bbdfSmrg                int count, i;
66135c4bbdfSmrg                NSString *name, *nname;
66235c4bbdfSmrg
66335c4bbdfSmrg                /* Localize the names in the default apps menu. */
66435c4bbdfSmrg
66535c4bbdfSmrg                apps =
66635c4bbdfSmrg                    [(NSDictionary *) defaults objectForKey:@PREFS_APPSMENU];
66735c4bbdfSmrg                if (apps != nil) {
66835c4bbdfSmrg                    count = [apps count];
66935c4bbdfSmrg                    for (i = 0; i < count; i++) {
67035c4bbdfSmrg                        elt = [apps objectAtIndex:i];
67135c4bbdfSmrg                        if (elt != nil &&
67235c4bbdfSmrg                            [elt isKindOfClass:[NSArray class]]) {
67335c4bbdfSmrg                            name = [elt objectAtIndex:0];
67435c4bbdfSmrg                            if (name != nil) {
67535c4bbdfSmrg                                nname = NSLocalizedString(name, nil);
67635c4bbdfSmrg                                if (nname != nil && nname != name)
67735c4bbdfSmrg                                    [elt replaceObjectAtIndex:0 withObject:
67835c4bbdfSmrg                                     nname];
67935c4bbdfSmrg                            }
68035c4bbdfSmrg                        }
68135c4bbdfSmrg                    }
68235c4bbdfSmrg                }
68335c4bbdfSmrg            }
68435c4bbdfSmrg        }
68535c4bbdfSmrg
68635c4bbdfSmrg        if (defaults != NULL) value = CFDictionaryGetValue(defaults, key);
68735c4bbdfSmrg        if (value != NULL) CFRetain(value);
68835c4bbdfSmrg    }
6894642e01fSmrg
69035c4bbdfSmrg    return value;
69135c4bbdfSmrg}
6924642e01fSmrg
69335c4bbdfSmrg- (int) prefs_get_integer:(NSString *)key default:(int)def
69435c4bbdfSmrg{
69535c4bbdfSmrg    CFPropertyListRef value;
69635c4bbdfSmrg    int ret;
6974642e01fSmrg
69835c4bbdfSmrg    value = [self prefs_get_copy:key];
6994642e01fSmrg
70035c4bbdfSmrg    if (value != NULL && CFGetTypeID(value) == CFNumberGetTypeID())
70135c4bbdfSmrg        CFNumberGetValue(value, kCFNumberIntType, &ret);
70235c4bbdfSmrg    else if (value != NULL && CFGetTypeID(value) == CFStringGetTypeID())
70335c4bbdfSmrg        ret = CFStringGetIntValue(value);
70435c4bbdfSmrg    else
70535c4bbdfSmrg        ret = def;
7064642e01fSmrg
70735c4bbdfSmrg    if (value != NULL) CFRelease(value);
70835c4bbdfSmrg
70935c4bbdfSmrg    return ret;
7104642e01fSmrg}
7114642e01fSmrg
71235c4bbdfSmrg- (const char *) prefs_get_string:(NSString *)key default:(const char *)def
71335c4bbdfSmrg{
7144642e01fSmrg    CFPropertyListRef value;
71535c4bbdfSmrg    const char *ret = NULL;
71635c4bbdfSmrg
71735c4bbdfSmrg    value = [self prefs_get_copy:key];
71835c4bbdfSmrg
71935c4bbdfSmrg    if (value != NULL && CFGetTypeID(value) == CFStringGetTypeID()) {
72035c4bbdfSmrg        NSString *s = (NSString *)value;
72135c4bbdfSmrg
72235c4bbdfSmrg        ret = [s UTF8String];
7234642e01fSmrg    }
7244642e01fSmrg
72535c4bbdfSmrg    if (value != NULL) CFRelease(value);
7264642e01fSmrg
72735c4bbdfSmrg    return ret != NULL ? ret : def;
7284642e01fSmrg}
7294642e01fSmrg
73035c4bbdfSmrg- (NSURL *) prefs_copy_url:(NSString *)key default:(NSURL *)def
73135c4bbdfSmrg{
7326747b715Smrg    CFPropertyListRef value;
7336747b715Smrg    NSURL *ret = NULL;
73435c4bbdfSmrg
7356747b715Smrg    value = [self prefs_get_copy:key];
73635c4bbdfSmrg
73735c4bbdfSmrg    if (value != NULL && CFGetTypeID(value) == CFStringGetTypeID()) {
73835c4bbdfSmrg        NSString *s = (NSString *)value;
7396747b715Smrg
7406747b715Smrg        ret = [NSURL URLWithString:s];
7416747b715Smrg        [ret retain];
7426747b715Smrg    }
74335c4bbdfSmrg
74435c4bbdfSmrg    if (value != NULL) CFRelease(value);
74535c4bbdfSmrg
7466747b715Smrg    return ret != NULL ? ret : def;
7476747b715Smrg}
7486747b715Smrg
74935c4bbdfSmrg- (float) prefs_get_float:(NSString *)key default:(float)def
75035c4bbdfSmrg{
75135c4bbdfSmrg    CFPropertyListRef value;
75235c4bbdfSmrg    float ret = def;
75335c4bbdfSmrg
75435c4bbdfSmrg    value = [self prefs_get_copy:key];
75535c4bbdfSmrg
75635c4bbdfSmrg    if (value != NULL
75735c4bbdfSmrg        && CFGetTypeID(value) == CFNumberGetTypeID()
75835c4bbdfSmrg        && CFNumberIsFloatType(value))
75935c4bbdfSmrg        CFNumberGetValue(value, kCFNumberFloatType, &ret);
76035c4bbdfSmrg    else if (value != NULL && CFGetTypeID(value) == CFStringGetTypeID())
76135c4bbdfSmrg        ret = CFStringGetDoubleValue(value);
76235c4bbdfSmrg
76335c4bbdfSmrg    if (value != NULL) CFRelease(value);
76435c4bbdfSmrg
76535c4bbdfSmrg    return ret;
7664642e01fSmrg}
7674642e01fSmrg
76835c4bbdfSmrg- (int) prefs_get_boolean:(NSString *)key default:(int)def
76935c4bbdfSmrg{
77035c4bbdfSmrg    CFPropertyListRef value;
77135c4bbdfSmrg    int ret = def;
77235c4bbdfSmrg
77335c4bbdfSmrg    value = [self prefs_get_copy:key];
77435c4bbdfSmrg
77535c4bbdfSmrg    if (value != NULL) {
77635c4bbdfSmrg        if (CFGetTypeID(value) == CFNumberGetTypeID())
77735c4bbdfSmrg            CFNumberGetValue(value, kCFNumberIntType, &ret);
77835c4bbdfSmrg        else if (CFGetTypeID(value) == CFBooleanGetTypeID())
77935c4bbdfSmrg            ret = CFBooleanGetValue(value);
78035c4bbdfSmrg        else if (CFGetTypeID(value) == CFStringGetTypeID()) {
78135c4bbdfSmrg            const char *tem = [(NSString *) value UTF8String];
78235c4bbdfSmrg            if (strcasecmp(tem, "true") == 0 || strcasecmp(tem, "yes") == 0)
78335c4bbdfSmrg                ret = YES;
78435c4bbdfSmrg            else
78535c4bbdfSmrg                ret = NO;
78635c4bbdfSmrg        }
78735c4bbdfSmrg
78835c4bbdfSmrg        CFRelease(value);
7894642e01fSmrg    }
79035c4bbdfSmrg    return ret;
7914642e01fSmrg}
7924642e01fSmrg
79335c4bbdfSmrg- (NSArray *) prefs_get_array:(NSString *)key
79435c4bbdfSmrg{
79535c4bbdfSmrg    NSArray *ret = nil;
79635c4bbdfSmrg    CFPropertyListRef value;
79735c4bbdfSmrg
79835c4bbdfSmrg    value = [self prefs_get_copy:key];
79935c4bbdfSmrg
80035c4bbdfSmrg    if (value != NULL) {
80135c4bbdfSmrg        if (CFGetTypeID(value) == CFArrayGetTypeID())
80235c4bbdfSmrg            ret = [cfarray_to_nsarray (value)autorelease];
80335c4bbdfSmrg
80435c4bbdfSmrg        CFRelease(value);
80535c4bbdfSmrg    }
80635c4bbdfSmrg
80735c4bbdfSmrg    return ret;
8084642e01fSmrg}
8094642e01fSmrg
81035c4bbdfSmrg- (void) prefs_set_integer:(NSString *)key value:(int)value
81135c4bbdfSmrg{
8124642e01fSmrg    CFNumberRef x;
81335c4bbdfSmrg
81435c4bbdfSmrg    x = CFNumberCreate(NULL, kCFNumberIntType, &value);
81535c4bbdfSmrg
81635c4bbdfSmrg    CFPreferencesSetValue((CFStringRef)key, (CFTypeRef)x,
81735c4bbdfSmrg                          app_prefs_domain_cfstr,
81835c4bbdfSmrg                          kCFPreferencesCurrentUser,
81935c4bbdfSmrg                          kCFPreferencesAnyHost);
82035c4bbdfSmrg
82135c4bbdfSmrg    CFRelease(x);
8224642e01fSmrg}
8234642e01fSmrg
82435c4bbdfSmrg- (void) prefs_set_float:(NSString *)key value:(float)value
82535c4bbdfSmrg{
8264642e01fSmrg    CFNumberRef x;
82735c4bbdfSmrg
82835c4bbdfSmrg    x = CFNumberCreate(NULL, kCFNumberFloatType, &value);
82935c4bbdfSmrg
83035c4bbdfSmrg    CFPreferencesSetValue((CFStringRef)key, (CFTypeRef)x,
83135c4bbdfSmrg                          app_prefs_domain_cfstr,
83235c4bbdfSmrg                          kCFPreferencesCurrentUser,
83335c4bbdfSmrg                          kCFPreferencesAnyHost);
83435c4bbdfSmrg
83535c4bbdfSmrg    CFRelease(x);
8364642e01fSmrg}
8374642e01fSmrg
83835c4bbdfSmrg- (void) prefs_set_boolean:(NSString *)key value:(int)value
83935c4bbdfSmrg{
84035c4bbdfSmrg    CFPreferencesSetValue(
84135c4bbdfSmrg        (CFStringRef)key,
84235c4bbdfSmrg        (CFTypeRef)(value ? kCFBooleanTrue
84335c4bbdfSmrg                    : kCFBooleanFalse),
84435c4bbdfSmrg        app_prefs_domain_cfstr,
84535c4bbdfSmrg        kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
84635c4bbdfSmrg
8474642e01fSmrg}
8484642e01fSmrg
84935c4bbdfSmrg- (void) prefs_set_array:(NSString *)key value:(NSArray *)value
85035c4bbdfSmrg{
85135c4bbdfSmrg    CFArrayRef cfarray;
85235c4bbdfSmrg
85335c4bbdfSmrg    cfarray = nsarray_to_cfarray(value);
85435c4bbdfSmrg    CFPreferencesSetValue((CFStringRef)key,
85535c4bbdfSmrg                          (CFTypeRef)cfarray,
85635c4bbdfSmrg                          app_prefs_domain_cfstr,
85735c4bbdfSmrg                          kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
85835c4bbdfSmrg    CFRelease(cfarray);
8594642e01fSmrg}
8604642e01fSmrg
86135c4bbdfSmrg- (void) prefs_set_string:(NSString *)key value:(NSString *)value
86235c4bbdfSmrg{
86335c4bbdfSmrg    CFPreferencesSetValue((CFStringRef)key, (CFTypeRef)value,
86435c4bbdfSmrg                          app_prefs_domain_cfstr, kCFPreferencesCurrentUser,
86535c4bbdfSmrg                          kCFPreferencesAnyHost);
8664642e01fSmrg}
8674642e01fSmrg
86835c4bbdfSmrg- (void) prefs_synchronize
86935c4bbdfSmrg{
87035c4bbdfSmrg    CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
8714642e01fSmrg}
8724642e01fSmrg
8734642e01fSmrg- (void) read_defaults
8744642e01fSmrg{
8754642e01fSmrg    NSString *nsstr;
8764642e01fSmrg    const char *tem;
87735c4bbdfSmrg
8786747b715Smrg    XQuartzRootlessDefault = [self prefs_get_boolean:@PREFS_ROOTLESS
87935c4bbdfSmrg                              default               :XQuartzRootlessDefault];
8806747b715Smrg    XQuartzFullscreenMenu = [self prefs_get_boolean:@PREFS_FULLSCREEN_MENU
88135c4bbdfSmrg                             default               :XQuartzFullscreenMenu];
88235c4bbdfSmrg    XQuartzFullscreenDisableHotkeys =
88335c4bbdfSmrg        ![self prefs_get_boolean:@PREFS_FULLSCREEN_HOTKEYS
88435c4bbdfSmrg          default               :!
88535c4bbdfSmrg          XQuartzFullscreenDisableHotkeys];
8864642e01fSmrg    darwinFakeButtons = [self prefs_get_boolean:@PREFS_FAKEBUTTONS
88735c4bbdfSmrg                         default               :darwinFakeButtons];
8886747b715Smrg    XQuartzOptionSendsAlt = [self prefs_get_boolean:@PREFS_OPTION_SENDS_ALT
88935c4bbdfSmrg                             default               :XQuartzOptionSendsAlt];
8906747b715Smrg
8914642e01fSmrg    if (darwinFakeButtons) {
8924642e01fSmrg        const char *fake2, *fake3;
8934642e01fSmrg
8944642e01fSmrg        fake2 = [self prefs_get_string:@PREFS_FAKE_BUTTON2 default:NULL];
8954642e01fSmrg        fake3 = [self prefs_get_string:@PREFS_FAKE_BUTTON3 default:NULL];
8964642e01fSmrg
89735c4bbdfSmrg        if (fake2 != NULL) darwinFakeMouse2Mask = DarwinParseModifierList(
89835c4bbdfSmrg                fake2, TRUE);
89935c4bbdfSmrg        if (fake3 != NULL) darwinFakeMouse3Mask = DarwinParseModifierList(
90035c4bbdfSmrg                fake3, TRUE);
9014642e01fSmrg    }
9024642e01fSmrg
9034642e01fSmrg    tem = [self prefs_get_string:@PREFS_APPKIT_MODIFIERS default:NULL];
9044642e01fSmrg    if (tem != NULL) darwinAppKitModMask = DarwinParseModifierList(tem, TRUE);
90535c4bbdfSmrg
9064642e01fSmrg    tem = [self prefs_get_string:@PREFS_WINDOW_ITEM_MODIFIERS default:NULL];
9074642e01fSmrg    if (tem != NULL) {
9084642e01fSmrg        windowItemModMask = DarwinParseModifierList(tem, FALSE);
90935c4bbdfSmrg    }
91035c4bbdfSmrg    else {
91135c4bbdfSmrg        nsstr = NSLocalizedString(@"window item modifiers",
91235c4bbdfSmrg                                  @"window item modifiers");
91335c4bbdfSmrg        if (nsstr != NULL) {
9144642e01fSmrg            tem = [nsstr UTF8String];
91535c4bbdfSmrg            if ((tem != NULL) && strcmp(tem, "window item modifiers")) {
9164642e01fSmrg                windowItemModMask = DarwinParseModifierList(tem, FALSE);
9174642e01fSmrg            }
9184642e01fSmrg        }
9194642e01fSmrg    }
9204642e01fSmrg
9216747b715Smrg    XQuartzEnableKeyEquivalents = [self prefs_get_boolean:@PREFS_KEYEQUIVS
92235c4bbdfSmrg                                   default               :
92335c4bbdfSmrg                                   XQuartzEnableKeyEquivalents];
92435c4bbdfSmrg
9254642e01fSmrg    darwinSyncKeymap = [self prefs_get_boolean:@PREFS_SYNC_KEYMAP
92635c4bbdfSmrg                        default               :darwinSyncKeymap];
92735c4bbdfSmrg
9284642e01fSmrg    darwinDesiredDepth = [self prefs_get_integer:@PREFS_DEPTH
92935c4bbdfSmrg                          default               :darwinDesiredDepth];
93035c4bbdfSmrg
9314642e01fSmrg    noTestExtensions = ![self prefs_get_boolean:@PREFS_TEST_EXTENSIONS
93235c4bbdfSmrg                         default               :FALSE];
93335c4bbdfSmrg
93435c4bbdfSmrg    noRenderExtension = ![self prefs_get_boolean:@PREFS_RENDER_EXTENSION
93535c4bbdfSmrg                          default               :TRUE];
93635c4bbdfSmrg
93735c4bbdfSmrg    XQuartzScrollInDeviceDirection =
93835c4bbdfSmrg        [self prefs_get_boolean:@PREFS_SCROLL_IN_DEV_DIRECTION
93935c4bbdfSmrg         default               :
94035c4bbdfSmrg         XQuartzScrollInDeviceDirection];
9416747b715Smrg
9426747b715Smrg#if XQUARTZ_SPARKLE
94335c4bbdfSmrg    NSURL *url = [self prefs_copy_url:@PREFS_UPDATE_FEED default:nil];
94435c4bbdfSmrg    if (url) {
9456747b715Smrg        [[SUUpdater sharedUpdater] setFeedURL:url];
9466747b715Smrg        [url release];
9476747b715Smrg    }
9486747b715Smrg#endif
9494642e01fSmrg}
9504642e01fSmrg
9514642e01fSmrg/* This will end up at the end of the responder chain. */
95235c4bbdfSmrg- (void) copy:sender
95335c4bbdfSmrg{
95435c4bbdfSmrg    DarwinSendDDXEvent(kXquartzPasteboardNotify, 1,
95535c4bbdfSmrg                       AppleWMCopyToPasteboard);
9564642e01fSmrg}
9574642e01fSmrg
95835c4bbdfSmrg- (X11Controller *) controller
95935c4bbdfSmrg{
9606747b715Smrg    return _controller;
9616747b715Smrg}
9626747b715Smrg
96335c4bbdfSmrg- (OSX_BOOL) x_active
96435c4bbdfSmrg{
9654642e01fSmrg    return _x_active;
9664642e01fSmrg}
9674642e01fSmrg
9684642e01fSmrg@end
9694642e01fSmrg
9704642e01fSmrgstatic NSArray *
97135c4bbdfSmrgarray_with_strings_and_numbers(int nitems, const char **items,
97235c4bbdfSmrg                               const char *numbers)
97335c4bbdfSmrg{
97435c4bbdfSmrg    NSMutableArray *array, *subarray;
97535c4bbdfSmrg    NSString *string, *number;
97635c4bbdfSmrg    int i;
97735c4bbdfSmrg
97835c4bbdfSmrg    /* (Can't autorelease on the X server thread) */
97935c4bbdfSmrg
98035c4bbdfSmrg    array = [[NSMutableArray alloc] initWithCapacity:nitems];
98135c4bbdfSmrg
98235c4bbdfSmrg    for (i = 0; i < nitems; i++) {
98335c4bbdfSmrg        subarray = [[NSMutableArray alloc] initWithCapacity:2];
98435c4bbdfSmrg
98535c4bbdfSmrg        string = [[NSString alloc] initWithUTF8String:items[i]];
98635c4bbdfSmrg        [subarray addObject:string];
98735c4bbdfSmrg        [string release];
98835c4bbdfSmrg
98935c4bbdfSmrg        if (numbers[i] != 0) {
99035c4bbdfSmrg            number = [[NSString alloc] initWithFormat:@"%d", numbers[i]];
99135c4bbdfSmrg            [subarray addObject:number];
99235c4bbdfSmrg            [number release];
99335c4bbdfSmrg        }
99435c4bbdfSmrg        else
99535c4bbdfSmrg            [subarray addObject:@""];
99635c4bbdfSmrg
99735c4bbdfSmrg        [array addObject:subarray];
99835c4bbdfSmrg        [subarray release];
99935c4bbdfSmrg    }
100035c4bbdfSmrg
100135c4bbdfSmrg    return array;
10024642e01fSmrg}
10034642e01fSmrg
100435c4bbdfSmrgvoid
100535c4bbdfSmrgX11ApplicationSetWindowMenu(int nitems, const char **items,
100635c4bbdfSmrg                            const char *shortcuts)
100735c4bbdfSmrg{
100835c4bbdfSmrg    NSArray *array;
100935c4bbdfSmrg    array = array_with_strings_and_numbers(nitems, items, shortcuts);
101035c4bbdfSmrg
101135c4bbdfSmrg    /* Send the array of strings over to the appkit thread */
101235c4bbdfSmrg
101335c4bbdfSmrg    message_kit_thread(@selector (set_window_menu:), array);
101435c4bbdfSmrg    [array release];
10154642e01fSmrg}
10164642e01fSmrg
101735c4bbdfSmrgvoid
101835c4bbdfSmrgX11ApplicationSetWindowMenuCheck(int idx)
101935c4bbdfSmrg{
102035c4bbdfSmrg    NSNumber *n;
102135c4bbdfSmrg
102235c4bbdfSmrg    n = [[NSNumber alloc] initWithInt:idx];
102335c4bbdfSmrg
102435c4bbdfSmrg    message_kit_thread(@selector (set_window_menu_check:), n);
102535c4bbdfSmrg
102635c4bbdfSmrg    [n release];
10274642e01fSmrg}
10284642e01fSmrg
102935c4bbdfSmrgvoid
103035c4bbdfSmrgX11ApplicationSetFrontProcess(void)
103135c4bbdfSmrg{
103235c4bbdfSmrg    message_kit_thread(@selector (set_front_process:), nil);
10334642e01fSmrg}
10344642e01fSmrg
103535c4bbdfSmrgvoid
103635c4bbdfSmrgX11ApplicationSetCanQuit(int state)
103735c4bbdfSmrg{
10384642e01fSmrg    NSNumber *n;
103935c4bbdfSmrg
10404642e01fSmrg    n = [[NSNumber alloc] initWithBool:state];
104135c4bbdfSmrg
104235c4bbdfSmrg    message_kit_thread(@selector (set_can_quit:), n);
104335c4bbdfSmrg
10444642e01fSmrg    [n release];
10454642e01fSmrg}
10464642e01fSmrg
104735c4bbdfSmrgvoid
104835c4bbdfSmrgX11ApplicationServerReady(void)
104935c4bbdfSmrg{
105035c4bbdfSmrg    message_kit_thread(@selector (server_ready:), nil);
10514642e01fSmrg}
10524642e01fSmrg
105335c4bbdfSmrgvoid
105435c4bbdfSmrgX11ApplicationShowHideMenubar(int state)
105535c4bbdfSmrg{
10564642e01fSmrg    NSNumber *n;
105735c4bbdfSmrg
10584642e01fSmrg    n = [[NSNumber alloc] initWithBool:state];
105935c4bbdfSmrg
106035c4bbdfSmrg    message_kit_thread(@selector (show_hide_menubar:), n);
106135c4bbdfSmrg
10624642e01fSmrg    [n release];
10634642e01fSmrg}
10644642e01fSmrg
106535c4bbdfSmrgvoid
106635c4bbdfSmrgX11ApplicationLaunchClient(const char *cmd)
106735c4bbdfSmrg{
10686747b715Smrg    NSString *string;
106935c4bbdfSmrg
10706747b715Smrg    string = [[NSString alloc] initWithUTF8String:cmd];
107135c4bbdfSmrg
107235c4bbdfSmrg    message_kit_thread(@selector (launch_client:), string);
107335c4bbdfSmrg
10746747b715Smrg    [string release];
10756747b715Smrg}
10766747b715Smrg
10778223e2f2Smrg/* This is a special function in that it is run from the *SERVER* thread and
10788223e2f2Smrg * not the AppKit thread.  We want to block entering a screen-capturing RandR
10798223e2f2Smrg * mode until we notify the user about how to get out if the X11 client crashes.
10808223e2f2Smrg */
108135c4bbdfSmrgBool
108235c4bbdfSmrgX11ApplicationCanEnterRandR(void)
108335c4bbdfSmrg{
10848223e2f2Smrg    NSString *title, *msg;
108535c4bbdfSmrg
108635c4bbdfSmrg    if ([X11App prefs_get_boolean:@PREFS_NO_RANDR_ALERT default:NO] ||
108735c4bbdfSmrg        XQuartzShieldingWindowLevel != 0)
10888223e2f2Smrg        return TRUE;
10898223e2f2Smrg
109035c4bbdfSmrg    title = NSLocalizedString(@"Enter RandR mode?",
109135c4bbdfSmrg                              @"Dialog title when switching to RandR");
109235c4bbdfSmrg    msg = NSLocalizedString(
109335c4bbdfSmrg        @"An application has requested X11 to change the resolution of your display.  X11 will restore the display to its previous state when the requesting application requests to return to the previous state.  Alternatively, you can use the ⌥⌘A key sequence to force X11 to return to the previous state.",
109435c4bbdfSmrg        @"Dialog when switching to RandR");
109535c4bbdfSmrg
109635c4bbdfSmrg    if (!XQuartzIsRootless)
10978223e2f2Smrg        QuartzShowFullscreen(FALSE);
10988223e2f2Smrg
109935c4bbdfSmrg    switch (NSRunAlertPanel(title, @"%@",
110035c4bbdfSmrg                            NSLocalizedString(@"Allow",
110135c4bbdfSmrg                                              @""),
110235c4bbdfSmrg                            NSLocalizedString(@"Cancel",
110335c4bbdfSmrg                                              @""),
110435c4bbdfSmrg                            NSLocalizedString(@"Always Allow", @""), msg)) {
110535c4bbdfSmrg    case NSAlertOtherReturn:
110635c4bbdfSmrg        [X11App prefs_set_boolean:@PREFS_NO_RANDR_ALERT value:YES];
110735c4bbdfSmrg        [X11App prefs_synchronize];
110835c4bbdfSmrg
110935c4bbdfSmrg    case NSAlertDefaultReturn:
111035c4bbdfSmrg        return YES;
111135c4bbdfSmrg
111235c4bbdfSmrg    default:
111335c4bbdfSmrg        return NO;
11148223e2f2Smrg    }
11158223e2f2Smrg}
11168223e2f2Smrg
111735c4bbdfSmrgstatic void
111835c4bbdfSmrgcheck_xinitrc(void)
111935c4bbdfSmrg{
11204642e01fSmrg    char *tem, buf[1024];
11214642e01fSmrg    NSString *msg;
112235c4bbdfSmrg
11234642e01fSmrg    if ([X11App prefs_get_boolean:@PREFS_DONE_XINIT_CHECK default:NO])
112435c4bbdfSmrg        return;
112535c4bbdfSmrg
112635c4bbdfSmrg    tem = getenv("HOME");
11274642e01fSmrg    if (tem == NULL) goto done;
112835c4bbdfSmrg
112935c4bbdfSmrg    snprintf(buf, sizeof(buf), "%s/.xinitrc", tem);
113035c4bbdfSmrg    if (access(buf, F_OK) != 0)
113135c4bbdfSmrg        goto done;
113235c4bbdfSmrg
113335c4bbdfSmrg    msg =
113435c4bbdfSmrg        NSLocalizedString(
113535c4bbdfSmrg            @"You have an existing ~/.xinitrc file.\n\n\
113635c4bbdfSmrg                             Windows displayed by X11 applications may not have titlebars, or may look \
113735c4bbdfSmrg                             different to windows displayed by native applications.\n\n\
113835c4bbdfSmrg                             Would you like to move aside the existing file and use the standard X11 \
113935c4bbdfSmrg                             environment the next time you start X11?"                                                                                                                                                                                                                                                                                                                                                                  ,
114035c4bbdfSmrg            @"Startup xinitrc dialog");
114135c4bbdfSmrg
114235c4bbdfSmrg    if (NSAlertDefaultReturn ==
114335c4bbdfSmrg        NSRunAlertPanel(nil, @"%@", NSLocalizedString(@"Yes", @""),
114435c4bbdfSmrg                        NSLocalizedString(@"No", @""), nil, msg)) {
11454642e01fSmrg        char buf2[1024];
11464642e01fSmrg        int i = -1;
11474642e01fSmrg
114835c4bbdfSmrg        snprintf(buf2, sizeof(buf2), "%s.old", buf);
114935c4bbdfSmrg
115035c4bbdfSmrg        for (i = 1; access(buf2, F_OK) == 0; i++)
115135c4bbdfSmrg            snprintf(buf2, sizeof(buf2), "%s.old.%d", buf, i);
115235c4bbdfSmrg
115335c4bbdfSmrg        rename(buf, buf2);
11544642e01fSmrg    }
115535c4bbdfSmrg
115635c4bbdfSmrgdone:
11574642e01fSmrg    [X11App prefs_set_boolean:@PREFS_DONE_XINIT_CHECK value:YES];
11584642e01fSmrg    [X11App prefs_synchronize];
11594642e01fSmrg}
11604642e01fSmrg
116135c4bbdfSmrgstatic inline pthread_t
116235c4bbdfSmrgcreate_thread(void *(*func)(void *), void *arg)
116335c4bbdfSmrg{
11646747b715Smrg    pthread_attr_t attr;
11656747b715Smrg    pthread_t tid;
116635c4bbdfSmrg
11676747b715Smrg    pthread_attr_init(&attr);
11686747b715Smrg    pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
11696747b715Smrg    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
11706747b715Smrg    pthread_create(&tid, &attr, func, arg);
11716747b715Smrg    pthread_attr_destroy(&attr);
117235c4bbdfSmrg
11736747b715Smrg    return tid;
11746747b715Smrg}
11756747b715Smrg
117635c4bbdfSmrgstatic void *
117735c4bbdfSmrgxpbproxy_x_thread(void *args)
117835c4bbdfSmrg{
11796747b715Smrg    xpbproxy_run();
11806747b715Smrg
118135c4bbdfSmrg    ErrorF("xpbproxy thread is terminating unexpectedly.\n");
11826747b715Smrg    return NULL;
11836747b715Smrg}
11846747b715Smrg
118535c4bbdfSmrgvoid
118635c4bbdfSmrgX11ApplicationMain(int argc, char **argv, char **envp)
118735c4bbdfSmrg{
11884642e01fSmrg    NSAutoreleasePool *pool;
11894642e01fSmrg
11904642e01fSmrg#ifdef DEBUG
119135c4bbdfSmrg    while (access("/tmp/x11-block", F_OK) == 0) sleep(1);
11924642e01fSmrg#endif
119335c4bbdfSmrg
11944642e01fSmrg    pool = [[NSAutoreleasePool alloc] init];
119535c4bbdfSmrg    X11App = (X11Application *)[X11Application sharedApplication];
119635c4bbdfSmrg    init_ports();
119735c4bbdfSmrg
119835c4bbdfSmrg    app_prefs_domain_cfstr =
119935c4bbdfSmrg        (CFStringRef)[[NSBundle mainBundle] bundleIdentifier];
12004642e01fSmrg
1201475c125cSmrg    if (app_prefs_domain_cfstr == NULL) {
120235c4bbdfSmrg        ErrorF(
120335c4bbdfSmrg            "X11ApplicationMain: Unable to determine bundle identifier.  Your installation of XQuartz may be broken.\n");
120435c4bbdfSmrg        app_prefs_domain_cfstr = CFSTR(BUNDLE_ID_PREFIX ".X11");
1205475c125cSmrg    }
1206475c125cSmrg
12074642e01fSmrg    [NSApp read_defaults];
12084642e01fSmrg    [NSBundle loadNibNamed:@"main" owner:NSApp];
12094642e01fSmrg    [[NSNotificationCenter defaultCenter] addObserver:NSApp
121035c4bbdfSmrg                                             selector:@selector (became_key:)
121135c4bbdfSmrg                                                 name:
121235c4bbdfSmrg     NSWindowDidBecomeKeyNotification object:nil];
12134642e01fSmrg
12144642e01fSmrg    /*
12154642e01fSmrg     * The xpr Quartz mode is statically linked into this server.
12164642e01fSmrg     * Initialize all the Quartz functions.
12174642e01fSmrg     */
12184642e01fSmrg    QuartzModeBundleInit();
12194642e01fSmrg
12204642e01fSmrg    /* Calculate the height of the menubar so we can avoid it. */
122135c4bbdfSmrg    aquaMenuBarHeight = [[NSApp mainMenu] menuBarHeight];
122235c4bbdfSmrg#if ! __LP64__
122335c4bbdfSmrg    if (!aquaMenuBarHeight) {
122435c4bbdfSmrg        aquaMenuBarHeight = [NSMenuView menuBarHeight];
122535c4bbdfSmrg    }
122635c4bbdfSmrg#endif
122735c4bbdfSmrg    if (!aquaMenuBarHeight) {
122835c4bbdfSmrg        NSScreen* primaryScreen = [[NSScreen screens] objectAtIndex:0];
122935c4bbdfSmrg        aquaMenuBarHeight = NSHeight([primaryScreen frame]) - NSMaxY([primaryScreen visibleFrame]);
123035c4bbdfSmrg    }
123135c4bbdfSmrg
123235c4bbdfSmrg#ifdef HAVE_LIBDISPATCH
123335c4bbdfSmrg    eventTranslationQueue = dispatch_queue_create(
123435c4bbdfSmrg        BUNDLE_ID_PREFIX ".X11.NSEventsToX11EventsQueue", NULL);
123535c4bbdfSmrg    assert(eventTranslationQueue != NULL);
123635c4bbdfSmrg#endif
12374642e01fSmrg
12384642e01fSmrg    /* Set the key layout seed before we start the server */
12394642e01fSmrg#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
124035c4bbdfSmrg    last_key_layout = TISCopyCurrentKeyboardLayoutInputSource();
12414642e01fSmrg
124235c4bbdfSmrg    if (!last_key_layout)
124335c4bbdfSmrg        ErrorF(
124435c4bbdfSmrg            "X11ApplicationMain: Unable to determine TISCopyCurrentKeyboardLayoutInputSource() at startup.\n");
12454642e01fSmrg#else
12464642e01fSmrg    KLGetCurrentKeyboardLayout(&last_key_layout);
124735c4bbdfSmrg    if (!last_key_layout)
124835c4bbdfSmrg        ErrorF(
124935c4bbdfSmrg            "X11ApplicationMain: Unable to determine KLGetCurrentKeyboardLayout() at startup.\n");
12504642e01fSmrg#endif
12514642e01fSmrg
12526747b715Smrg    if (!QuartsResyncKeymap(FALSE)) {
125335c4bbdfSmrg        ErrorF("X11ApplicationMain: Could not build a valid keymap.\n");
12544642e01fSmrg    }
12554642e01fSmrg
12564642e01fSmrg    /* Tell the server thread that it can proceed */
12574642e01fSmrg    QuartzInitServer(argc, argv, envp);
125835c4bbdfSmrg
12594642e01fSmrg    /* This must be done after QuartzInitServer because it can result in
12604642e01fSmrg     * an mieqEnqueue() - <rdar://problem/6300249>
12614642e01fSmrg     */
12624642e01fSmrg    check_xinitrc();
126335c4bbdfSmrg
12646747b715Smrg    create_thread(xpbproxy_x_thread, NULL);
12656747b715Smrg
12666747b715Smrg#if XQUARTZ_SPARKLE
12676747b715Smrg    [[X11App controller] setup_sparkle];
12686747b715Smrg    [[SUUpdater sharedUpdater] resetUpdateCycle];
126935c4bbdfSmrg    //    [[SUUpdater sharedUpdater] checkForUpdates:X11App];
12706747b715Smrg#endif
12716747b715Smrg
12726747b715Smrg    [pool release];
12734642e01fSmrg    [NSApp run];
12744642e01fSmrg    /* not reached */
12754642e01fSmrg}
12764642e01fSmrg
12774642e01fSmrg@implementation X11Application (Private)
12784642e01fSmrg
12794642e01fSmrg#ifdef NX_DEVICELCMDKEYMASK
12804642e01fSmrg/* This is to workaround a bug in the VNC server where we sometimes see the L
12814642e01fSmrg * modifier and sometimes see no "side"
12824642e01fSmrg */
128335c4bbdfSmrgstatic inline int
128435c4bbdfSmrgensure_flag(int flags, int device_independent, int device_dependents,
128535c4bbdfSmrg            int device_dependent_default)
128635c4bbdfSmrg{
128735c4bbdfSmrg    if ((flags & device_independent) &&
128835c4bbdfSmrg        !(flags & device_dependents))
12894642e01fSmrg        flags |= device_dependent_default;
12904642e01fSmrg    return flags;
12914642e01fSmrg}
12924642e01fSmrg#endif
12934642e01fSmrg
12949ace9065Smrg#ifdef DEBUG_UNTRUSTED_POINTER_DELTA
129535c4bbdfSmrgstatic const char *
129635c4bbdfSmrguntrusted_str(NSEvent *e)
129735c4bbdfSmrg{
129835c4bbdfSmrg    switch ([e type]) {
129935c4bbdfSmrg    case NSScrollWheel:
130035c4bbdfSmrg        return "NSScrollWheel";
130135c4bbdfSmrg
130235c4bbdfSmrg    case NSTabletPoint:
130335c4bbdfSmrg        return "NSTabletPoint";
130435c4bbdfSmrg
130535c4bbdfSmrg    case NSOtherMouseDown:
130635c4bbdfSmrg        return "NSOtherMouseDown";
130735c4bbdfSmrg
130835c4bbdfSmrg    case NSOtherMouseUp:
130935c4bbdfSmrg        return "NSOtherMouseUp";
131035c4bbdfSmrg
131135c4bbdfSmrg    case NSLeftMouseDown:
131235c4bbdfSmrg        return "NSLeftMouseDown";
131335c4bbdfSmrg
131435c4bbdfSmrg    case NSLeftMouseUp:
131535c4bbdfSmrg        return "NSLeftMouseUp";
131635c4bbdfSmrg
131735c4bbdfSmrg    default:
131835c4bbdfSmrg        switch ([e subtype]) {
131935c4bbdfSmrg        case NSTabletPointEventSubtype:
132035c4bbdfSmrg            return "NSTabletPointEventSubtype";
132135c4bbdfSmrg
132235c4bbdfSmrg        case NSTabletProximityEventSubtype:
132335c4bbdfSmrg            return "NSTabletProximityEventSubtype";
132435c4bbdfSmrg
13259ace9065Smrg        default:
132635c4bbdfSmrg            return "Other";
132735c4bbdfSmrg        }
13289ace9065Smrg    }
13299ace9065Smrg}
13309ace9065Smrg#endif
13319ace9065Smrg
133235c4bbdfSmrgextern void
13331b5d61b8Smrgwait_for_mieq_init(void);
133435c4bbdfSmrg
133535c4bbdfSmrg- (void) sendX11NSEvent:(NSEvent *)e
133635c4bbdfSmrg{
133735c4bbdfSmrg    NSPoint location = NSZeroPoint;
13384642e01fSmrg    int ev_button, ev_type;
133935c4bbdfSmrg    static float pressure = 0.0;       // static so ProximityOut will have the value from the previous tablet event
134035c4bbdfSmrg    static NSPoint tilt;               // static so ProximityOut will have the value from the previous tablet event
134135c4bbdfSmrg    static DeviceIntPtr darwinTabletCurrent = NULL;
134235c4bbdfSmrg    static BOOL needsProximityIn = NO; // Do we do need to handle a pending ProximityIn once we have pressure/tilt?
13434642e01fSmrg    DeviceIntPtr pDev;
13444642e01fSmrg    int modifierFlags;
13456747b715Smrg    BOOL isMouseOrTabletEvent, isTabletEvent;
13464642e01fSmrg
134735c4bbdfSmrg    if (!darwinTabletCurrent) {
134835c4bbdfSmrg        /* Ensure that the event system is initialized */
13491b5d61b8Smrg        wait_for_mieq_init();
135035c4bbdfSmrg        assert(darwinTabletStylus);
135135c4bbdfSmrg
135235c4bbdfSmrg        tilt = NSZeroPoint;
135335c4bbdfSmrg        darwinTabletCurrent = darwinTabletStylus;
135435c4bbdfSmrg    }
135535c4bbdfSmrg
135635c4bbdfSmrg    isMouseOrTabletEvent = [e type] == NSLeftMouseDown ||
135735c4bbdfSmrg                           [e type] == NSOtherMouseDown ||
135835c4bbdfSmrg                           [e type] == NSRightMouseDown ||
135935c4bbdfSmrg                           [e type] == NSLeftMouseUp ||
136035c4bbdfSmrg                           [e type] == NSOtherMouseUp ||
136135c4bbdfSmrg                           [e type] == NSRightMouseUp ||
136235c4bbdfSmrg                           [e type] == NSLeftMouseDragged ||
136335c4bbdfSmrg                           [e type] == NSOtherMouseDragged ||
136435c4bbdfSmrg                           [e type] == NSRightMouseDragged ||
136535c4bbdfSmrg                           [e type] == NSMouseMoved ||
136635c4bbdfSmrg                           [e type] == NSTabletPoint || 
136735c4bbdfSmrg                           [e type] == NSScrollWheel;
13684642e01fSmrg
13696747b715Smrg    isTabletEvent = ([e type] == NSTabletPoint) ||
137035c4bbdfSmrg                    (isMouseOrTabletEvent &&
137135c4bbdfSmrg                     ([e subtype] == NSTabletPointEventSubtype ||
137235c4bbdfSmrg                      [e subtype] == NSTabletProximityEventSubtype));
13734642e01fSmrg
137435c4bbdfSmrg    if (isMouseOrTabletEvent) {
13756747b715Smrg        static NSPoint lastpt;
13766747b715Smrg        NSWindow *window = [e window];
13776747b715Smrg        NSRect screen = [[[NSScreen screens] objectAtIndex:0] frame];
137835c4bbdfSmrg        BOOL hasUntrustedPointerDelta;
137935c4bbdfSmrg
13806747b715Smrg        // NSEvents for tablets are not consistent wrt deltaXY between events, so we cannot rely on that
13816747b715Smrg        // Thus tablets will be subject to the warp-pointer bug worked around by the delta, but tablets
13826747b715Smrg        // are not normally used in cases where that bug would present itself, so this is a fair tradeoff
13836747b715Smrg        // <rdar://problem/7111003> deltaX and deltaY are incorrect for NSMouseMoved, NSTabletPointEventSubtype
13846747b715Smrg        // http://xquartz.macosforge.org/trac/ticket/288
13856747b715Smrg        hasUntrustedPointerDelta = isTabletEvent;
138635c4bbdfSmrg
13876747b715Smrg        // The deltaXY for middle click events also appear erroneous after fast user switching
13886747b715Smrg        // <rdar://problem/7979468> deltaX and deltaY are incorrect for NSOtherMouseDown and NSOtherMouseUp after FUS
13896747b715Smrg        // http://xquartz.macosforge.org/trac/ticket/389
139035c4bbdfSmrg        hasUntrustedPointerDelta |= [e type] == NSOtherMouseDown ||
139135c4bbdfSmrg                                    [e type] == NSOtherMouseUp;
13926747b715Smrg
13936747b715Smrg        // The deltaXY for scroll events correspond to the scroll delta, not the pointer delta
13946747b715Smrg        // <rdar://problem/7989690> deltaXY for wheel events are being sent as mouse movement
139535c4bbdfSmrg        hasUntrustedPointerDelta |= [e type] == NSScrollWheel;
13969ace9065Smrg
13979ace9065Smrg#ifdef DEBUG_UNTRUSTED_POINTER_DELTA
139835c4bbdfSmrg        hasUntrustedPointerDelta |= [e type] == NSLeftMouseDown ||
139935c4bbdfSmrg                                    [e type] == NSLeftMouseUp;
14009ace9065Smrg#endif
140135c4bbdfSmrg
140235c4bbdfSmrg        if (window != nil) {
14036747b715Smrg            NSRect frame = [window frame];
14046747b715Smrg            location = [e locationInWindow];
14056747b715Smrg            location.x += frame.origin.x;
14066747b715Smrg            location.y += frame.origin.y;
14076747b715Smrg            lastpt = location;
140835c4bbdfSmrg        }
140935c4bbdfSmrg        else if (hasUntrustedPointerDelta) {
14109ace9065Smrg#ifdef DEBUG_UNTRUSTED_POINTER_DELTA
14119ace9065Smrg            ErrorF("--- Begin Event Debug ---\n");
14129ace9065Smrg            ErrorF("Event type: %s\n", untrusted_str(e));
14139ace9065Smrg            ErrorF("old lastpt: (%0.2f, %0.2f)\n", lastpt.x, lastpt.y);
14149ace9065Smrg            ErrorF("     delta: (%0.2f, %0.2f)\n", [e deltaX], -[e deltaY]);
141535c4bbdfSmrg            ErrorF("  location: (%0.2f, %0.2f)\n", lastpt.x + [e deltaX],
141635c4bbdfSmrg                   lastpt.y - [e deltaY]);
141735c4bbdfSmrg            ErrorF("workaround: (%0.2f, %0.2f)\n", [e locationInWindow].x,
141835c4bbdfSmrg                   [e locationInWindow].y);
14199ace9065Smrg            ErrorF("--- End Event Debug ---\n");
14209ace9065Smrg
14219ace9065Smrg            location.x = lastpt.x + [e deltaX];
14229ace9065Smrg            location.y = lastpt.y - [e deltaY];
14239ace9065Smrg            lastpt = [e locationInWindow];
14249ace9065Smrg#else
14256747b715Smrg            location = [e locationInWindow];
14266747b715Smrg            lastpt = location;
14279ace9065Smrg#endif
142835c4bbdfSmrg        }
142935c4bbdfSmrg        else {
14306747b715Smrg            location.x = lastpt.x + [e deltaX];
14316747b715Smrg            location.y = lastpt.y - [e deltaY];
14326747b715Smrg            lastpt = [e locationInWindow];
14336747b715Smrg        }
143435c4bbdfSmrg
14356747b715Smrg        /* Convert coordinate system */
14366747b715Smrg        location.y = (screen.origin.y + screen.size.height) - location.y;
14376747b715Smrg    }
143835c4bbdfSmrg
14394642e01fSmrg    modifierFlags = [e modifierFlags];
144035c4bbdfSmrg
14414642e01fSmrg#ifdef NX_DEVICELCMDKEYMASK
14424642e01fSmrg    /* This is to workaround a bug in the VNC server where we sometimes see the L
14434642e01fSmrg     * modifier and sometimes see no "side"
14444642e01fSmrg     */
144535c4bbdfSmrg    modifierFlags = ensure_flag(modifierFlags, NX_CONTROLMASK,
144635c4bbdfSmrg                                NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK,
144735c4bbdfSmrg                                NX_DEVICELCTLKEYMASK);
144835c4bbdfSmrg    modifierFlags = ensure_flag(modifierFlags, NX_SHIFTMASK,
144935c4bbdfSmrg                                NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK, 
145035c4bbdfSmrg                                NX_DEVICELSHIFTKEYMASK);
145135c4bbdfSmrg    modifierFlags = ensure_flag(modifierFlags, NX_COMMANDMASK,
145235c4bbdfSmrg                                NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK,
145335c4bbdfSmrg                                NX_DEVICELCMDKEYMASK);
145435c4bbdfSmrg    modifierFlags = ensure_flag(modifierFlags, NX_ALTERNATEMASK,
145535c4bbdfSmrg                                NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK,
145635c4bbdfSmrg                                NX_DEVICELALTKEYMASK);
14574642e01fSmrg#endif
14584642e01fSmrg
14596747b715Smrg    modifierFlags &= darwin_all_modifier_mask;
14604642e01fSmrg
14614642e01fSmrg    /* We don't receive modifier key events while out of focus, and 3button
14624642e01fSmrg     * emulation mucks this up, so we need to check our modifier flag state
14634642e01fSmrg     * on every event... ugg
14644642e01fSmrg     */
146535c4bbdfSmrg
146635c4bbdfSmrg    if (darwin_all_modifier_flags != modifierFlags)
14674642e01fSmrg        DarwinUpdateModKeys(modifierFlags);
14684642e01fSmrg
146935c4bbdfSmrg    switch ([e type]) {
147035c4bbdfSmrg    case NSLeftMouseDown:
147135c4bbdfSmrg        ev_button = 1;
147235c4bbdfSmrg        ev_type = ButtonPress;
147335c4bbdfSmrg        goto handle_mouse;
147435c4bbdfSmrg
147535c4bbdfSmrg    case NSOtherMouseDown:
147635c4bbdfSmrg        ev_button = 2;
147735c4bbdfSmrg        ev_type = ButtonPress;
147835c4bbdfSmrg        goto handle_mouse;
147935c4bbdfSmrg
148035c4bbdfSmrg    case NSRightMouseDown:
148135c4bbdfSmrg        ev_button = 3;
148235c4bbdfSmrg        ev_type = ButtonPress;
148335c4bbdfSmrg        goto handle_mouse;
148435c4bbdfSmrg
148535c4bbdfSmrg    case NSLeftMouseUp:
148635c4bbdfSmrg        ev_button = 1;
148735c4bbdfSmrg        ev_type = ButtonRelease;
148835c4bbdfSmrg        goto handle_mouse;
148935c4bbdfSmrg
149035c4bbdfSmrg    case NSOtherMouseUp:
149135c4bbdfSmrg        ev_button = 2;
149235c4bbdfSmrg        ev_type = ButtonRelease;
149335c4bbdfSmrg        goto handle_mouse;
149435c4bbdfSmrg
149535c4bbdfSmrg    case NSRightMouseUp:
149635c4bbdfSmrg        ev_button = 3;
149735c4bbdfSmrg        ev_type = ButtonRelease;
149835c4bbdfSmrg        goto handle_mouse;
149935c4bbdfSmrg
150035c4bbdfSmrg    case NSLeftMouseDragged:
150135c4bbdfSmrg        ev_button = 1;
150235c4bbdfSmrg        ev_type = MotionNotify;
150335c4bbdfSmrg        goto handle_mouse;
150435c4bbdfSmrg
150535c4bbdfSmrg    case NSOtherMouseDragged:
150635c4bbdfSmrg        ev_button = 2;
150735c4bbdfSmrg        ev_type = MotionNotify;
150835c4bbdfSmrg        goto handle_mouse;
150935c4bbdfSmrg
151035c4bbdfSmrg    case NSRightMouseDragged:
151135c4bbdfSmrg        ev_button = 3;
151235c4bbdfSmrg        ev_type = MotionNotify;
151335c4bbdfSmrg        goto handle_mouse;
151435c4bbdfSmrg
151535c4bbdfSmrg    case NSMouseMoved:
151635c4bbdfSmrg        ev_button = 0;
151735c4bbdfSmrg        ev_type = MotionNotify;
151835c4bbdfSmrg        goto handle_mouse;
151935c4bbdfSmrg
152035c4bbdfSmrg    case NSTabletPoint:
152135c4bbdfSmrg        ev_button = 0;
152235c4bbdfSmrg        ev_type = MotionNotify;
152335c4bbdfSmrg        goto handle_mouse;
152435c4bbdfSmrg
152535c4bbdfSmrghandle_mouse:
152635c4bbdfSmrg        pDev = darwinPointer;
152735c4bbdfSmrg
152835c4bbdfSmrg        /* NSTabletPoint can have no subtype */
152935c4bbdfSmrg        if ([e type] != NSTabletPoint &&
153035c4bbdfSmrg            [e subtype] == NSTabletProximityEventSubtype) {
153135c4bbdfSmrg            switch ([e pointingDeviceType]) {
153235c4bbdfSmrg            case NSEraserPointingDevice:
153335c4bbdfSmrg                darwinTabletCurrent = darwinTabletEraser;
153435c4bbdfSmrg                break;
153535c4bbdfSmrg
153635c4bbdfSmrg            case NSPenPointingDevice:
153735c4bbdfSmrg                darwinTabletCurrent = darwinTabletStylus;
153835c4bbdfSmrg                break;
153935c4bbdfSmrg
154035c4bbdfSmrg            case NSCursorPointingDevice:
154135c4bbdfSmrg            case NSUnknownPointingDevice:
154235c4bbdfSmrg            default:
154335c4bbdfSmrg                darwinTabletCurrent = darwinTabletCursor;
154435c4bbdfSmrg                break;
15454642e01fSmrg            }
15464642e01fSmrg
154735c4bbdfSmrg            if ([e isEnteringProximity])
154835c4bbdfSmrg                needsProximityIn = YES;
154935c4bbdfSmrg            else
155035c4bbdfSmrg                DarwinSendTabletEvents(darwinTabletCurrent, ProximityOut, 0,
155135c4bbdfSmrg                                       location.x, location.y, pressure,
155235c4bbdfSmrg                                       tilt.x, tilt.y);
155335c4bbdfSmrg            return;
155435c4bbdfSmrg        }
155535c4bbdfSmrg
155635c4bbdfSmrg        if ([e type] == NSTabletPoint ||
155735c4bbdfSmrg            [e subtype] == NSTabletPointEventSubtype) {
155835c4bbdfSmrg            pressure = [e pressure];
155935c4bbdfSmrg            tilt = [e tilt];
156035c4bbdfSmrg
156135c4bbdfSmrg            pDev = darwinTabletCurrent;
156235c4bbdfSmrg
156335c4bbdfSmrg            if (needsProximityIn) {
156435c4bbdfSmrg                DarwinSendTabletEvents(darwinTabletCurrent, ProximityIn, 0,
156535c4bbdfSmrg                                       location.x, location.y, pressure,
156635c4bbdfSmrg                                       tilt.x, tilt.y);
156735c4bbdfSmrg
156835c4bbdfSmrg                needsProximityIn = NO;
15694642e01fSmrg            }
157035c4bbdfSmrg        }
15714642e01fSmrg
157235c4bbdfSmrg        if (!XQuartzServerVisible && noTestExtensions) {
15736747b715Smrg#if defined(XPLUGIN_VERSION) && XPLUGIN_VERSION > 0
157435c4bbdfSmrg            /* Older libXplugin (Tiger/"Stock" Leopard) aren't thread safe, so we can't call xp_find_window from the Appkit thread */
157535c4bbdfSmrg            xp_window_id wid = 0;
157635c4bbdfSmrg            xp_error err;
157735c4bbdfSmrg
157835c4bbdfSmrg            /* Sigh. Need to check that we're really over one of
157935c4bbdfSmrg             * our windows. (We need to receive pointer events while
158035c4bbdfSmrg             * not in the foreground, but we don't want to receive them
158135c4bbdfSmrg             * when another window is over us or we might show a tooltip)
158235c4bbdfSmrg             */
15836747b715Smrg
158435c4bbdfSmrg            err = xp_find_window(location.x, location.y, 0, &wid);
15856747b715Smrg
158635c4bbdfSmrg            if (err != XP_Success || (err == XP_Success && wid == 0))
15874642e01fSmrg#endif
158835c4bbdfSmrg            {
158935c4bbdfSmrg                bgMouseLocation = location;
159035c4bbdfSmrg                bgMouseLocationUpdated = TRUE;
159135c4bbdfSmrg                return;
15926747b715Smrg            }
159335c4bbdfSmrg        }
159435c4bbdfSmrg
159535c4bbdfSmrg        if (bgMouseLocationUpdated) {
159635c4bbdfSmrg            if (!(ev_type == MotionNotify && ev_button == 0)) {
159735c4bbdfSmrg                DarwinSendPointerEvents(darwinPointer, MotionNotify, 0,
159835c4bbdfSmrg                                        location.x, location.y,
159935c4bbdfSmrg                                        0.0, 0.0);
16006747b715Smrg            }
160135c4bbdfSmrg            bgMouseLocationUpdated = FALSE;
160235c4bbdfSmrg        }
16036747b715Smrg
160435c4bbdfSmrg        if (pDev == darwinPointer) {
160535c4bbdfSmrg            DarwinSendPointerEvents(pDev, ev_type, ev_button,
160635c4bbdfSmrg                                    location.x, location.y,
160735c4bbdfSmrg                                    [e deltaX], [e deltaY]);
160835c4bbdfSmrg        } else {
160935c4bbdfSmrg            DarwinSendTabletEvents(pDev, ev_type, ev_button,
161035c4bbdfSmrg                                   location.x, location.y, pressure,
161135c4bbdfSmrg                                   tilt.x, tilt.y);
161235c4bbdfSmrg        }
161335c4bbdfSmrg
161435c4bbdfSmrg        break;
161535c4bbdfSmrg
161635c4bbdfSmrg    case NSTabletProximity:
161735c4bbdfSmrg        switch ([e pointingDeviceType]) {
161835c4bbdfSmrg        case NSEraserPointingDevice:
161935c4bbdfSmrg            darwinTabletCurrent = darwinTabletEraser;
16204642e01fSmrg            break;
162135c4bbdfSmrg
162235c4bbdfSmrg        case NSPenPointingDevice:
162335c4bbdfSmrg            darwinTabletCurrent = darwinTabletStylus;
162435c4bbdfSmrg            break;
162535c4bbdfSmrg
162635c4bbdfSmrg        case NSCursorPointingDevice:
162735c4bbdfSmrg        case NSUnknownPointingDevice:
162835c4bbdfSmrg        default:
162935c4bbdfSmrg            darwinTabletCurrent = darwinTabletCursor;
16304642e01fSmrg            break;
163135c4bbdfSmrg        }
163235c4bbdfSmrg
163335c4bbdfSmrg        if ([e isEnteringProximity])
163435c4bbdfSmrg            needsProximityIn = YES;
163535c4bbdfSmrg        else
163635c4bbdfSmrg            DarwinSendTabletEvents(darwinTabletCurrent, ProximityOut, 0,
163735c4bbdfSmrg                                   location.x, location.y, pressure,
163835c4bbdfSmrg                                   tilt.x, tilt.y);
163935c4bbdfSmrg        break;
164035c4bbdfSmrg
164135c4bbdfSmrg    case NSScrollWheel:
164235c4bbdfSmrg    {
164335c4bbdfSmrg#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050
164435c4bbdfSmrg        float deltaX = [e deltaX];
164535c4bbdfSmrg        float deltaY = [e deltaY];
164635c4bbdfSmrg        BOOL isContinuous = NO;
164735c4bbdfSmrg#else
164835c4bbdfSmrg        CGFloat deltaX = [e deltaX];
164935c4bbdfSmrg        CGFloat deltaY = [e deltaY];
165035c4bbdfSmrg        CGEventRef cge = [e CGEvent];
165135c4bbdfSmrg        BOOL isContinuous =
165235c4bbdfSmrg            CGEventGetIntegerValueField(cge, kCGScrollWheelEventIsContinuous);
165335c4bbdfSmrg
165435c4bbdfSmrg#if 0
165535c4bbdfSmrg        /* Scale the scroll value by line height */
165635c4bbdfSmrg        CGEventSourceRef source = CGEventCreateSourceFromEvent(cge);
165735c4bbdfSmrg        if (source) {
165835c4bbdfSmrg            double lineHeight = CGEventSourceGetPixelsPerLine(source);
165935c4bbdfSmrg            CFRelease(source);
16604642e01fSmrg            
166135c4bbdfSmrg            /* There's no real reason for the 1/5 ratio here other than that
166235c4bbdfSmrg             * it feels like a good ratio after some testing.
166335c4bbdfSmrg             */
166435c4bbdfSmrg            
166535c4bbdfSmrg            deltaX *= lineHeight / 5.0;
166635c4bbdfSmrg            deltaY *= lineHeight / 5.0;
166735c4bbdfSmrg        }
166835c4bbdfSmrg#endif
166935c4bbdfSmrg#endif
167035c4bbdfSmrg        
16716747b715Smrg#if !defined(XPLUGIN_VERSION) || XPLUGIN_VERSION == 0
167235c4bbdfSmrg        /* If we're in the background, we need to send a MotionNotify event
167335c4bbdfSmrg         * first, since we aren't getting them on background mouse motion
167435c4bbdfSmrg         */
167535c4bbdfSmrg        if (!XQuartzServerVisible && noTestExtensions) {
167635c4bbdfSmrg            bgMouseLocationUpdated = FALSE;
167735c4bbdfSmrg            DarwinSendPointerEvents(darwinPointer, MotionNotify, 0,
167835c4bbdfSmrg                                    location.x, location.y,
167935c4bbdfSmrg                                    0.0, 0.0);
168035c4bbdfSmrg        }
16816747b715Smrg#endif
1682475c125cSmrg#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
168335c4bbdfSmrg        // TODO: Change 1117 to NSAppKitVersionNumber10_7 when it is defined
168435c4bbdfSmrg        if (NSAppKitVersionNumber >= 1117 &&
168535c4bbdfSmrg            XQuartzScrollInDeviceDirection &&
168635c4bbdfSmrg            [e isDirectionInvertedFromDevice]) {
168735c4bbdfSmrg            deltaX *= -1;
168835c4bbdfSmrg            deltaY *= -1;
168935c4bbdfSmrg        }
1690475c125cSmrg#endif
169135c4bbdfSmrg        /* This hack is in place to better deal with "clicky" scroll wheels:
169235c4bbdfSmrg         * http://xquartz.macosforge.org/trac/ticket/562
169335c4bbdfSmrg         */
169435c4bbdfSmrg        if (!isContinuous) {
169535c4bbdfSmrg            static NSTimeInterval lastScrollTime = 0.0;
169635c4bbdfSmrg
169735c4bbdfSmrg            /* These store how much extra we have already scrolled.
169835c4bbdfSmrg             * ie, this is how much we ignore on the next event.
169935c4bbdfSmrg             */
170035c4bbdfSmrg            static double deficit_x = 0.0;
170135c4bbdfSmrg            static double deficit_y = 0.0;
170235c4bbdfSmrg
170335c4bbdfSmrg            /* If we have past a second since the last scroll, wipe the slate
170435c4bbdfSmrg             * clean
170535c4bbdfSmrg             */
170635c4bbdfSmrg            if ([e timestamp] - lastScrollTime > 1.0) {
170735c4bbdfSmrg                deficit_x = deficit_y = 0.0;
1708475c125cSmrg            }
170935c4bbdfSmrg            lastScrollTime = [e timestamp];
171035c4bbdfSmrg
171135c4bbdfSmrg            if (deltaX != 0.0) {
171235c4bbdfSmrg                /* If we changed directions, wipe the slate clean */
171335c4bbdfSmrg                if ((deficit_x < 0.0 && deltaX > 0.0) ||
171435c4bbdfSmrg                    (deficit_x > 0.0 && deltaX < 0.0)) {
171535c4bbdfSmrg                    deficit_x = 0.0;
171635c4bbdfSmrg                }
171735c4bbdfSmrg
171835c4bbdfSmrg                /* Eat up the deficit, but ensure that something is
171935c4bbdfSmrg                 * always sent 
17206747b715Smrg                 */
172135c4bbdfSmrg                if (fabs(deltaX) > fabs(deficit_x)) {
172235c4bbdfSmrg                    deltaX -= deficit_x;
172335c4bbdfSmrg
172435c4bbdfSmrg                    if (deltaX > 0.0) {
172535c4bbdfSmrg                        deficit_x = ceil(deltaX) - deltaX;
172635c4bbdfSmrg                        deltaX = ceil(deltaX);
172735c4bbdfSmrg                    } else {
172835c4bbdfSmrg                        deficit_x = floor(deltaX) - deltaX;
172935c4bbdfSmrg                        deltaX = floor(deltaX);
173035c4bbdfSmrg                    }
173135c4bbdfSmrg                } else {
173235c4bbdfSmrg                    deficit_x -= deltaX;
173335c4bbdfSmrg
173435c4bbdfSmrg                    if (deltaX > 0.0) {
173535c4bbdfSmrg                        deltaX = 1.0;
173635c4bbdfSmrg                    } else {
173735c4bbdfSmrg                        deltaX = -1.0;
173835c4bbdfSmrg                    }
173935c4bbdfSmrg
174035c4bbdfSmrg                    deficit_x += deltaX;
17416747b715Smrg                }
17426747b715Smrg            }
17436747b715Smrg
174435c4bbdfSmrg            if (deltaY != 0.0) {
174535c4bbdfSmrg                /* If we changed directions, wipe the slate clean */
174635c4bbdfSmrg                if ((deficit_y < 0.0 && deltaY > 0.0) ||
174735c4bbdfSmrg                    (deficit_y > 0.0 && deltaY < 0.0)) {
174835c4bbdfSmrg                    deficit_y = 0.0;
174935c4bbdfSmrg                }
175035c4bbdfSmrg
175135c4bbdfSmrg                /* Eat up the deficit, but ensure that something is
175235c4bbdfSmrg                 * always sent 
175335c4bbdfSmrg                 */
175435c4bbdfSmrg                if (fabs(deltaY) > fabs(deficit_y)) {
175535c4bbdfSmrg                    deltaY -= deficit_y;
175635c4bbdfSmrg
175735c4bbdfSmrg                    if (deltaY > 0.0) {
175835c4bbdfSmrg                        deficit_y = ceil(deltaY) - deltaY;
175935c4bbdfSmrg                        deltaY = ceil(deltaY);
176035c4bbdfSmrg                    } else {
176135c4bbdfSmrg                        deficit_y = floor(deltaY) - deltaY;
176235c4bbdfSmrg                        deltaY = floor(deltaY);
176335c4bbdfSmrg                    }
17644642e01fSmrg                } else {
176535c4bbdfSmrg                    deficit_y -= deltaY;
176635c4bbdfSmrg
176735c4bbdfSmrg                    if (deltaY > 0.0) {
176835c4bbdfSmrg                        deltaY = 1.0;
176935c4bbdfSmrg                    } else {
177035c4bbdfSmrg                        deltaY = -1.0;
177135c4bbdfSmrg                    }
177235c4bbdfSmrg
177335c4bbdfSmrg                    deficit_y += deltaY;
177435c4bbdfSmrg                }
177535c4bbdfSmrg            }
177635c4bbdfSmrg        }
177735c4bbdfSmrg
177835c4bbdfSmrg        DarwinSendScrollEvents(deltaX, deltaY);
177935c4bbdfSmrg        break;
178035c4bbdfSmrg    }
178135c4bbdfSmrg
178235c4bbdfSmrg    case NSKeyDown:
178335c4bbdfSmrg    case NSKeyUp:
178435c4bbdfSmrg    {
178535c4bbdfSmrg        /* XKB clobbers our keymap at startup, so we need to force it on the first keypress.
178635c4bbdfSmrg         * TODO: Make this less of a kludge.
178735c4bbdfSmrg         */
178835c4bbdfSmrg        static int force_resync_keymap = YES;
178935c4bbdfSmrg        if (force_resync_keymap) {
179035c4bbdfSmrg            DarwinSendDDXEvent(kXquartzReloadKeymap, 0);
179135c4bbdfSmrg            force_resync_keymap = NO;
179235c4bbdfSmrg        }
179335c4bbdfSmrg    }
179435c4bbdfSmrg
179535c4bbdfSmrg        if (darwinSyncKeymap) {
179635c4bbdfSmrg#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
179735c4bbdfSmrg            TISInputSourceRef key_layout = 
179835c4bbdfSmrg                TISCopyCurrentKeyboardLayoutInputSource();
179935c4bbdfSmrg            TISInputSourceRef clear;
180035c4bbdfSmrg            if (CFEqual(key_layout, last_key_layout)) {
180135c4bbdfSmrg                CFRelease(key_layout);
180235c4bbdfSmrg            }
180335c4bbdfSmrg            else {
180435c4bbdfSmrg                /* Swap/free thread-safely */
180535c4bbdfSmrg                clear = last_key_layout;
180635c4bbdfSmrg                last_key_layout = key_layout;
180735c4bbdfSmrg                CFRelease(clear);
18084642e01fSmrg#else
180935c4bbdfSmrg            KeyboardLayoutRef key_layout;
181035c4bbdfSmrg            KLGetCurrentKeyboardLayout(&key_layout);
181135c4bbdfSmrg            if (key_layout != last_key_layout) {
181235c4bbdfSmrg                last_key_layout = key_layout;
18134642e01fSmrg#endif
181435c4bbdfSmrg                /* Update keyInfo */
181535c4bbdfSmrg                if (!QuartsResyncKeymap(TRUE)) {
181635c4bbdfSmrg                    ErrorF(
181735c4bbdfSmrg                        "sendX11NSEvent: Could not build a valid keymap.\n");
18184642e01fSmrg                }
18194642e01fSmrg            }
182035c4bbdfSmrg        }
18214642e01fSmrg
182235c4bbdfSmrg        ev_type = ([e type] == NSKeyDown) ? KeyPress : KeyRelease;
182335c4bbdfSmrg        DarwinSendKeyboardEvents(ev_type, [e keyCode]);
182435c4bbdfSmrg        break;
18254642e01fSmrg
182635c4bbdfSmrg    default:
182735c4bbdfSmrg        break;              /* for gcc */
182835c4bbdfSmrg    }
18294642e01fSmrg}
18304642e01fSmrg@end
1831