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#import "X11Application.h"
38dc61d50dSmrg#import "NSUserDefaults+XQuartzDefaults.h"
394642e01fSmrg
404642e01fSmrg#include "darwin.h"
416747b715Smrg#include "quartz.h"
424642e01fSmrg#include "darwinEvents.h"
434642e01fSmrg#include "quartzKeyboard.h"
446747b715Smrg#include <X11/extensions/applewmconst.h>
454642e01fSmrg#include "micmap.h"
466747b715Smrg#include "exglobals.h"
474642e01fSmrg
484642e01fSmrg#include <mach/mach.h>
494642e01fSmrg#include <unistd.h>
504642e01fSmrg
519ace9065Smrg#include <pthread.h>
529ace9065Smrg
534642e01fSmrg#include <Xplugin.h>
544642e01fSmrg
554642e01fSmrg// pbproxy/pbproxy.h
5635c4bbdfSmrgextern int
5735c4bbdfSmrgxpbproxy_run(void);
584642e01fSmrg
594642e01fSmrg#ifndef XSERVER_VERSION
604642e01fSmrg#define XSERVER_VERSION "?"
614642e01fSmrg#endif
624642e01fSmrg
6335c4bbdfSmrg#include <dispatch/dispatch.h>
6435c4bbdfSmrg
6535c4bbdfSmrgstatic dispatch_queue_t eventTranslationQueue;
6635c4bbdfSmrg
6735c4bbdfSmrg#ifndef __has_feature
6835c4bbdfSmrg#define __has_feature(x) 0
6935c4bbdfSmrg#endif
7035c4bbdfSmrg
7135c4bbdfSmrg#ifndef CF_RETURNS_RETAINED
7235c4bbdfSmrg#if __has_feature(attribute_cf_returns_retained)
7335c4bbdfSmrg#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
7435c4bbdfSmrg#else
7535c4bbdfSmrg#define CF_RETURNS_RETAINED
7635c4bbdfSmrg#endif
7735c4bbdfSmrg#endif
784642e01fSmrg
79a035e2b2Smrg#ifndef APPKIT_APPFLAGS_HACK
80a035e2b2Smrg#define APPKIT_APPFLAGS_HACK 1
81a035e2b2Smrg#endif
82a035e2b2Smrg
834642e01fSmrgextern Bool noTestExtensions;
8435c4bbdfSmrgextern Bool noRenderExtension;
854642e01fSmrg
864642e01fSmrgstatic TISInputSourceRef last_key_layout;
874642e01fSmrg
88475c125cSmrg/* This preference is only tested on Lion or later as it's not relevant to
89475c125cSmrg * earlier OS versions.
90475c125cSmrg */
91475c125cSmrgBool XQuartzScrollInDeviceDirection = FALSE;
92475c125cSmrg
934642e01fSmrgextern int darwinFakeButtons;
944642e01fSmrg
956747b715Smrg/* Store the mouse location while in the background, and update X11's pointer
966747b715Smrg * location when we become the foreground application
976747b715Smrg */
986747b715Smrgstatic NSPoint bgMouseLocation;
996747b715Smrgstatic BOOL bgMouseLocationUpdated = FALSE;
1006747b715Smrg
1014642e01fSmrgX11Application *X11App;
1024642e01fSmrg
10335c4bbdfSmrg#define ALL_KEY_MASKS (NSShiftKeyMask | NSControlKeyMask | \
10435c4bbdfSmrg                       NSAlternateKeyMask | NSCommandKeyMask)
1054642e01fSmrg
106a035e2b2Smrg#if APPKIT_APPFLAGS_HACK && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101500
107a035e2b2Smrg// This was removed from the SDK in 10.15
108a035e2b2Smrg@interface NSApplication () {
109a035e2b2Smrg@protected
110a035e2b2Smrg    /* All instance variables are private */
111a035e2b2Smrg    short               _running;
112a035e2b2Smrg    struct __appFlags {
113a035e2b2Smrg        unsigned int        _hidden:1;
114a035e2b2Smrg        unsigned int        _appleEventActivationInProgress:1;
115a035e2b2Smrg        unsigned int        _active:1;
116a035e2b2Smrg        unsigned int        _hasBeenRun:1;
117a035e2b2Smrg        unsigned int        _doingUnhide:1;
118a035e2b2Smrg        unsigned int        _delegateReturnsValidRequestor:1;
119a035e2b2Smrg        unsigned int        _deactPending:1;
120a035e2b2Smrg        unsigned int        _invalidState:1;
121a035e2b2Smrg        unsigned int        _invalidEvent:1;
122a035e2b2Smrg        unsigned int        _postedWindowsNeedUpdateNote:1;
123a035e2b2Smrg        unsigned int        _wantsToActivate:1;
124a035e2b2Smrg        unsigned int        _doingHide:1;
125a035e2b2Smrg        unsigned int        _dontSendShouldTerminate:1;
126a035e2b2Smrg        unsigned int        _ignoresFullScreen:1;
127a035e2b2Smrg        unsigned int        _finishedLaunching:1;
128a035e2b2Smrg        unsigned int        _hasEventDelegate:1;
129a035e2b2Smrg        unsigned int        _appTerminating:1;
130a035e2b2Smrg        unsigned int        _didNSOpenOrPrint:1;
131a035e2b2Smrg        unsigned int        _inDealloc:1;
132a035e2b2Smrg        unsigned int        _pendingDidFinish:1;
133a035e2b2Smrg        unsigned int        _hasKeyFocus:1;
134a035e2b2Smrg        unsigned int        _panelsNonactivating:1;
135a035e2b2Smrg        unsigned int        _hiddenOnLaunch:1;
136a035e2b2Smrg        unsigned int        _openStatus:2;
137a035e2b2Smrg        unsigned int        _batchOrdering:1;
138a035e2b2Smrg        unsigned int        _waitingForTerminationReply:1;
139a035e2b2Smrg        unsigned int        _unused:1;
140a035e2b2Smrg        unsigned int        _enumeratingMemoryPressureHandlers:1;
141a035e2b2Smrg        unsigned int        _didTryRestoringPersistentState:1;
142a035e2b2Smrg        unsigned int        _windowDragging:1;
143a035e2b2Smrg        unsigned int        _mightBeSwitching:1;
144a035e2b2Smrg    }                   _appFlags;
145a035e2b2Smrg    id                  _mainMenu;
146a035e2b2Smrg}
147a035e2b2Smrg@end
148a035e2b2Smrg#endif
149a035e2b2Smrg
150c8548ba8Smrg@interface NSApplication (Internal)
151c8548ba8Smrg- (void)_setKeyWindow:(id)window;
152c8548ba8Smrg- (void)_setMainWindow:(id)window;
153c8548ba8Smrg@end
154c8548ba8Smrg
1554642e01fSmrg@interface X11Application (Private)
1564642e01fSmrg- (void) sendX11NSEvent:(NSEvent *)e;
1574642e01fSmrg@end
1584642e01fSmrg
159c8548ba8Smrg@interface X11Application ()
160c8548ba8Smrg@property (nonatomic, readwrite, assign) OSX_BOOL x_active;
161c8548ba8Smrg@end
162c8548ba8Smrg
1634642e01fSmrg@implementation X11Application
1644642e01fSmrg
1654642e01fSmrgtypedef struct message_struct message;
1664642e01fSmrgstruct message_struct {
1674642e01fSmrg    mach_msg_header_t hdr;
1684642e01fSmrg    SEL selector;
1694642e01fSmrg    NSObject *arg;
1704642e01fSmrg};
1714642e01fSmrg
1724642e01fSmrg/* Quartz mode initialization routine. This is often dynamically loaded
1734642e01fSmrg   but is statically linked into this X server. */
17435c4bbdfSmrgBool
17535c4bbdfSmrgQuartzModeBundleInit(void);
1764642e01fSmrg
17735c4bbdfSmrg- (void) dealloc
17835c4bbdfSmrg{
179c8548ba8Smrg    self.controller = nil;
1804642e01fSmrg    [super dealloc];
1814642e01fSmrg}
1824642e01fSmrg
18335c4bbdfSmrg- (void) orderFrontStandardAboutPanel: (id) sender
18435c4bbdfSmrg{
1854642e01fSmrg    NSMutableDictionary *dict;
1864642e01fSmrg    NSDictionary *infoDict;
1874642e01fSmrg    NSString *tem;
18835c4bbdfSmrg
1894642e01fSmrg    dict = [NSMutableDictionary dictionaryWithCapacity:3];
1904642e01fSmrg    infoDict = [[NSBundle mainBundle] infoDictionary];
19135c4bbdfSmrg
19235c4bbdfSmrg    [dict setObject: NSLocalizedString(@"The X Window System", @"About panel")
19335c4bbdfSmrg             forKey:@"ApplicationName"];
19435c4bbdfSmrg
1954642e01fSmrg    tem = [infoDict objectForKey:@"CFBundleShortVersionString"];
19635c4bbdfSmrg
1974642e01fSmrg    [dict setObject:[NSString stringWithFormat:@"XQuartz %@", tem]
19835c4bbdfSmrg             forKey:@"ApplicationVersion"];
19935c4bbdfSmrg
20035c4bbdfSmrg    [dict setObject:[NSString stringWithFormat:@"xorg-server %s",
20135c4bbdfSmrg                     XSERVER_VERSION]
20235c4bbdfSmrg     forKey:@"Version"];
2034642e01fSmrg
2044642e01fSmrg    [self orderFrontStandardAboutPanelWithOptions: dict];
2054642e01fSmrg}
2064642e01fSmrg
20735c4bbdfSmrg- (void) activateX:(OSX_BOOL)state
20835c4bbdfSmrg{
209c8548ba8Smrg    OSX_BOOL const x_active = self.x_active;
210c8548ba8Smrg
211c8548ba8Smrg    if (x_active == state)
21235c4bbdfSmrg        return;
21335c4bbdfSmrg
214c8548ba8Smrg    DEBUG_LOG("state=%d, x_active=%d, \n", state, x_active);
2154642e01fSmrg    if (state) {
21635c4bbdfSmrg        if (bgMouseLocationUpdated) {
21735c4bbdfSmrg            DarwinSendPointerEvents(darwinPointer, MotionNotify, 0,
21835c4bbdfSmrg                                    bgMouseLocation.x, bgMouseLocation.y,
21935c4bbdfSmrg                                    0.0, 0.0);
2206747b715Smrg            bgMouseLocationUpdated = FALSE;
2214642e01fSmrg        }
2226747b715Smrg        DarwinSendDDXEvent(kXquartzActivate, 0);
22335c4bbdfSmrg    }
22435c4bbdfSmrg    else {
2254642e01fSmrg
22635c4bbdfSmrg        if (darwin_all_modifier_flags)
2274642e01fSmrg            DarwinUpdateModKeys(0);
22835c4bbdfSmrg
22935c4bbdfSmrg        DarwinInputReleaseButtonsAndKeys(darwinKeyboard);
23035c4bbdfSmrg        DarwinInputReleaseButtonsAndKeys(darwinPointer);
23135c4bbdfSmrg        DarwinInputReleaseButtonsAndKeys(darwinTabletCursor);
23235c4bbdfSmrg        DarwinInputReleaseButtonsAndKeys(darwinTabletStylus);
23335c4bbdfSmrg        DarwinInputReleaseButtonsAndKeys(darwinTabletEraser);
23435c4bbdfSmrg
2354642e01fSmrg        DarwinSendDDXEvent(kXquartzDeactivate, 0);
2364642e01fSmrg    }
2374642e01fSmrg
238c8548ba8Smrg    self.x_active = state;
2394642e01fSmrg}
2404642e01fSmrg
24135c4bbdfSmrg- (void) became_key:(NSWindow *)win
24235c4bbdfSmrg{
24335c4bbdfSmrg    [self activateX:NO];
2444642e01fSmrg}
2454642e01fSmrg
24635c4bbdfSmrg- (void) sendEvent:(NSEvent *)e
24735c4bbdfSmrg{
248c8548ba8Smrg    /* Don't try sending to X if we haven't initialized.  This can happen if AppKit takes over
249c8548ba8Smrg     * (eg: uncaught exception) early in launch.
250c8548ba8Smrg     */
251c8548ba8Smrg    if (!eventTranslationQueue) {
252c8548ba8Smrg        [super sendEvent:e];
253c8548ba8Smrg        return;
254c8548ba8Smrg    }
255c8548ba8Smrg
2564642e01fSmrg    OSX_BOOL for_appkit, for_x;
257c8548ba8Smrg    OSX_BOOL const x_active = self.x_active;
25835c4bbdfSmrg
2594642e01fSmrg    /* By default pass down the responder chain and to X. */
2604642e01fSmrg    for_appkit = YES;
2614642e01fSmrg    for_x = YES;
26235c4bbdfSmrg
2634642e01fSmrg    switch ([e type]) {
26435c4bbdfSmrg    case NSLeftMouseDown:
26535c4bbdfSmrg    case NSRightMouseDown:
26635c4bbdfSmrg    case NSOtherMouseDown:
26735c4bbdfSmrg    case NSLeftMouseUp:
26835c4bbdfSmrg    case NSRightMouseUp:
26935c4bbdfSmrg    case NSOtherMouseUp:
270dc61d50dSmrg    case NSScrollWheel:
271dc61d50dSmrg
27235c4bbdfSmrg        if ([e window] != nil) {
27335c4bbdfSmrg            /* Pointer event has an (AppKit) window. Probably something for the kit. */
27435c4bbdfSmrg            for_x = NO;
275c8548ba8Smrg            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
281c8548ba8Smrg            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;
355c8548ba8Smrg                } else if (XQuartzEnableKeyEquivalents &&
35635c4bbdfSmrg                         xp_is_symbolic_hotkey_event([e eventRef])) {
35735c4bbdfSmrg                    swallow_keycode = [e keyCode];
35835c4bbdfSmrg                    do_swallow = YES;
35935c4bbdfSmrg                    for_x = NO;
3604642e01fSmrg                }
36135c4bbdfSmrg                else if (XQuartzEnableKeyEquivalents &&
36235c4bbdfSmrg                         [[self mainMenu] performKeyEquivalent:e]) {
36335c4bbdfSmrg                    swallow_keycode = [e keyCode];
36435c4bbdfSmrg                    do_swallow = YES;
36535c4bbdfSmrg                    for_appkit = NO;
36635c4bbdfSmrg                    for_x = NO;
36735c4bbdfSmrg                }
36835c4bbdfSmrg                else if (!XQuartzIsRootless
36935c4bbdfSmrg                         && ([e modifierFlags] & ALL_KEY_MASKS) ==
37035c4bbdfSmrg                         (NSCommandKeyMask | NSAlternateKeyMask)
37135c4bbdfSmrg                         && ([e keyCode] == 0 /*a*/ || [e keyCode] ==
37235c4bbdfSmrg                             53 /*Esc*/)) {
37335c4bbdfSmrg                    /* We have this here to force processing fullscreen
37435c4bbdfSmrg                     * toggle even if XQuartzEnableKeyEquivalents is disabled */
37535c4bbdfSmrg                    swallow_keycode = [e keyCode];
37635c4bbdfSmrg                    do_swallow = YES;
37735c4bbdfSmrg                    for_x = NO;
37835c4bbdfSmrg                    for_appkit = NO;
37935c4bbdfSmrg                    DarwinSendDDXEvent(kXquartzToggleFullscreen, 0);
38035c4bbdfSmrg                }
38135c4bbdfSmrg                else {
38235c4bbdfSmrg                    /* No kit window is focused, so send it to X. */
38335c4bbdfSmrg                    for_appkit = NO;
3841b5d61b8Smrg
3851b5d61b8Smrg                    /* Reset our swallow state if we're seeing the same keyCode again.
3861b5d61b8Smrg                     * This can happen if we become !_x_active when the keyCode we
3871b5d61b8Smrg                     * intended to swallow is delivered.  See:
3881b5d61b8Smrg                     * https://bugs.freedesktop.org/show_bug.cgi?id=92648
3891b5d61b8Smrg                     */
3901b5d61b8Smrg                    if ([e keyCode] == swallow_keycode) {
3911b5d61b8Smrg                        do_swallow = NO;
3921b5d61b8Smrg                    }
39335c4bbdfSmrg                }
39435c4bbdfSmrg            }
39535c4bbdfSmrg            else {       /* KeyUp */
39635c4bbdfSmrg                /* If we saw a key equivalent on the down, don't pass
39735c4bbdfSmrg                 * the up through to X. */
39835c4bbdfSmrg                if (do_swallow && [e keyCode] == swallow_keycode) {
39935c4bbdfSmrg                    do_swallow = NO;
40035c4bbdfSmrg                    for_x = NO;
40135c4bbdfSmrg                }
40235c4bbdfSmrg            }
40335c4bbdfSmrg        }
40435c4bbdfSmrg        else {       /* !_x_active */
40535c4bbdfSmrg            for_x = NO;
40635c4bbdfSmrg        }
40735c4bbdfSmrg        break;
40835c4bbdfSmrg
40935c4bbdfSmrg    case NSFlagsChanged:
41035c4bbdfSmrg        /* Don't tell X11 about modifiers changing while it's not active */
41135c4bbdfSmrg        if (!_x_active)
41235c4bbdfSmrg            for_x = NO;
41335c4bbdfSmrg        break;
41435c4bbdfSmrg
41535c4bbdfSmrg    case NSAppKitDefined:
41635c4bbdfSmrg        switch ([e subtype]) {
41735c4bbdfSmrg            static BOOL x_was_active = NO;
41835c4bbdfSmrg
41935c4bbdfSmrg        case NSApplicationActivatedEventType:
42035c4bbdfSmrg            for_x = NO;
42135c4bbdfSmrg            if ([e window] == nil && x_was_active) {
422dc61d50dSmrg                BOOL order_all_windows = YES;
42335c4bbdfSmrg                for_appkit = NO;
42435c4bbdfSmrg
425a035e2b2Smrg#if APPKIT_APPFLAGS_HACK
42635c4bbdfSmrg                /* FIXME: This is a hack to avoid passing the event to AppKit which
42735c4bbdfSmrg                 *        would result in it raising one of its windows.
42835c4bbdfSmrg                 */
42935c4bbdfSmrg                _appFlags._active = YES;
430a035e2b2Smrg#endif
43135c4bbdfSmrg
43235c4bbdfSmrg                [self set_front_process:nil];
43335c4bbdfSmrg
43435c4bbdfSmrg                /* Get the Spaces preference for SwitchOnActivate */
435dc61d50dSmrg                BOOL const workspaces = [NSUserDefaults.dockDefaults boolForKey:@"workspaces"];
43635c4bbdfSmrg                if (workspaces) {
437dc61d50dSmrg                    order_all_windows = [NSUserDefaults.globalDefaults boolForKey:@"AppleSpacesSwitchOnActivate"];
43835c4bbdfSmrg                }
43935c4bbdfSmrg
44035c4bbdfSmrg                /* TODO: In the workspaces && !AppleSpacesSwitchOnActivate case, the windows are ordered
44135c4bbdfSmrg                 *       correctly, but we need to activate the top window on this space if there is
44235c4bbdfSmrg                 *       none active.
44335c4bbdfSmrg                 *
44435c4bbdfSmrg                 *       If there are no active windows, and there are minimized windows, we should
44535c4bbdfSmrg                 *       be restoring one of them.
44635c4bbdfSmrg                 */
44735c4bbdfSmrg                if ([e data2] & 0x10) {         // 0x10 (bfCPSOrderAllWindowsForward) is set when we use cmd-tab or the dock icon
448dc61d50dSmrg                    DarwinSendDDXEvent(kXquartzBringAllToFront, 1, order_all_windows);
44935c4bbdfSmrg                }
4504642e01fSmrg            }
4514642e01fSmrg            break;
45235c4bbdfSmrg
45335c4bbdfSmrg        case 18:         /* ApplicationDidReactivate */
45435c4bbdfSmrg            if (XQuartzFullscreenVisible) for_appkit = NO;
4554642e01fSmrg            break;
4569ace9065Smrg
45735c4bbdfSmrg        case NSApplicationDeactivatedEventType:
45835c4bbdfSmrg            for_x = NO;
4599ace9065Smrg
46035c4bbdfSmrg            x_was_active = _x_active;
46135c4bbdfSmrg            if (_x_active)
46235c4bbdfSmrg                [self activateX:NO];
4634642e01fSmrg            break;
46435c4bbdfSmrg        }
46535c4bbdfSmrg        break;
46635c4bbdfSmrg
46735c4bbdfSmrg    default:
46835c4bbdfSmrg        break;          /* for gcc */
4694642e01fSmrg    }
47035c4bbdfSmrg
471dc61d50dSmrg    if (for_appkit) {
472dc61d50dSmrg        [super sendEvent:e];
473dc61d50dSmrg    }
47435c4bbdfSmrg
47535c4bbdfSmrg    if (for_x) {
47635c4bbdfSmrg        dispatch_async(eventTranslationQueue, ^{
477dc61d50dSmrg            [self sendX11NSEvent:e];
478dc61d50dSmrg        });
47935c4bbdfSmrg    }
4804642e01fSmrg}
4814642e01fSmrg
48235c4bbdfSmrg- (void) set_apps_menu:(NSArray *)list
48335c4bbdfSmrg{
484c8548ba8Smrg    [self.controller set_apps_menu:list];
4854642e01fSmrg}
4864642e01fSmrg
48735c4bbdfSmrg- (void) set_front_process:unused
48835c4bbdfSmrg{
48935c4bbdfSmrg    [NSApp activateIgnoringOtherApps:YES];
4904642e01fSmrg
49135c4bbdfSmrg    if ([self modalWindow] == nil)
49235c4bbdfSmrg        [self activateX:YES];
4934642e01fSmrg}
4944642e01fSmrg
49535c4bbdfSmrg- (void) show_hide_menubar:(NSNumber *)state
49635c4bbdfSmrg{
4974642e01fSmrg    /* Also shows/hides the dock */
4984642e01fSmrg    if ([state boolValue])
49935c4bbdfSmrg        SetSystemUIMode(kUIModeNormal, 0);
5004642e01fSmrg    else
50135c4bbdfSmrg        SetSystemUIMode(kUIModeAllHidden,
50235c4bbdfSmrg                        XQuartzFullscreenMenu ? kUIOptionAutoShowMenuBar : 0);                   // kUIModeAllSuppressed or kUIOptionAutoShowMenuBar can be used to allow "mouse-activation"
5034642e01fSmrg}
5044642e01fSmrg
50535c4bbdfSmrg- (void) launch_client:(NSString *)cmd
50635c4bbdfSmrg{
507c8548ba8Smrg    (void)[self.controller application:self openFile:cmd];
5086747b715Smrg}
5094642e01fSmrg
51035c4bbdfSmrg
511dc61d50dSmrg- (void) read_defaults
51235c4bbdfSmrg{
513dc61d50dSmrg    NSUserDefaults * const defaults = NSUserDefaults.xquartzDefaults;
5144642e01fSmrg
515dc61d50dSmrg    XQuartzRootlessDefault = [defaults boolForKey:XQuartzPrefKeyRootless];
516dc61d50dSmrg    XQuartzFullscreenMenu = [defaults boolForKey:XQuartzPrefKeyFullscreenMenu];
517dc61d50dSmrg    XQuartzFullscreenDisableHotkeys = ![defaults boolForKey:XQuartzPrefKeyFullscreenHotkeys];
5184642e01fSmrg
519dc61d50dSmrg    darwinFakeButtons = [defaults boolForKey:XQuartzPrefKeyFakeButtons];
520dc61d50dSmrg    XQuartzOptionSendsAlt = [defaults boolForKey:XQuartzPrefKeyOptionSendsAlt];
5214642e01fSmrg
522dc61d50dSmrg    if (darwinFakeButtons) {
523dc61d50dSmrg        NSString * const fake2 = [defaults stringForKey:XQuartzPrefKeyFakeButton2];
524dc61d50dSmrg        if (fake2) {
525dc61d50dSmrg            darwinFakeMouse2Mask = DarwinParseModifierList(fake2.UTF8String, TRUE);
52635c4bbdfSmrg        }
52735c4bbdfSmrg
528dc61d50dSmrg        NSString * const fake3 = [defaults stringForKey:XQuartzPrefKeyFakeButton3];
529dc61d50dSmrg        if (fake3) {
530dc61d50dSmrg            darwinFakeMouse3Mask = DarwinParseModifierList(fake3.UTF8String, TRUE);
53135c4bbdfSmrg        }
53235c4bbdfSmrg    }
53335c4bbdfSmrg
534dc61d50dSmrg    NSString * const appKitModifiers = [defaults stringForKey:XQuartzPrefKeyAppKitModifiers];
535dc61d50dSmrg    if (appKitModifiers) {
536dc61d50dSmrg        darwinAppKitModMask = DarwinParseModifierList(appKitModifiers.UTF8String, TRUE);
5374642e01fSmrg    }
5384642e01fSmrg
539dc61d50dSmrg    NSString * const windowItemModifiers = [defaults stringForKey:XQuartzPrefKeyWindowItemModifiers];
540dc61d50dSmrg    if (windowItemModifiers) {
541dc61d50dSmrg        windowItemModMask = DarwinParseModifierList(windowItemModifiers.UTF8String, FALSE);
54235c4bbdfSmrg    }
54335c4bbdfSmrg
544dc61d50dSmrg    XQuartzEnableKeyEquivalents = [defaults boolForKey:XQuartzPrefKeyKeyEquivs];
54535c4bbdfSmrg
546dc61d50dSmrg    darwinSyncKeymap = [defaults boolForKey:XQuartzPrefKeySyncKeymap];
54735c4bbdfSmrg
548dc61d50dSmrg    darwinDesiredDepth = [defaults integerForKey:XQuartzPrefKeyDepth];
54935c4bbdfSmrg
550dc61d50dSmrg    noTestExtensions = ![defaults boolForKey:XQuartzPrefKeyTESTExtension];
551dc61d50dSmrg    noRenderExtension = ![defaults boolForKey:XQuartzPrefKeyRENDERExtension];
55235c4bbdfSmrg
553dc61d50dSmrg    XQuartzScrollInDeviceDirection = [defaults boolForKey:XQuartzPrefKeyScrollInDeviceDirection];
5544642e01fSmrg}
5554642e01fSmrg
5564642e01fSmrg/* This will end up at the end of the responder chain. */
55735c4bbdfSmrg- (void) copy:sender
55835c4bbdfSmrg{
55935c4bbdfSmrg    DarwinSendDDXEvent(kXquartzPasteboardNotify, 1,
56035c4bbdfSmrg                       AppleWMCopyToPasteboard);
5614642e01fSmrg}
5624642e01fSmrg
5634642e01fSmrg@end
5644642e01fSmrg
565c8548ba8Smrgvoid
566c8548ba8SmrgX11ApplicationSetWindowMenu(int nitems, const char **items,
567c8548ba8Smrg                            const char *shortcuts)
56835c4bbdfSmrg{
569c8548ba8Smrg    @autoreleasepool {
570c8548ba8Smrg        NSMutableArray <NSArray <NSString *> *> * const allMenuItems = [NSMutableArray array];
57135c4bbdfSmrg
572c8548ba8Smrg        for (int i = 0; i < nitems; i++) {
573c8548ba8Smrg            NSMutableArray <NSString *> * const menuItem = [NSMutableArray array];
574c8548ba8Smrg            [menuItem addObject:@(items[i])];
57535c4bbdfSmrg
576c8548ba8Smrg            if (shortcuts[i] == 0) {
577c8548ba8Smrg                [menuItem addObject:@""];
578c8548ba8Smrg            } else {
579c8548ba8Smrg                [menuItem addObject:[NSString stringWithFormat:@"%d", shortcuts[i]]];
580c8548ba8Smrg            }
58135c4bbdfSmrg
582c8548ba8Smrg            [allMenuItems addObject:menuItem];
58335c4bbdfSmrg        }
58435c4bbdfSmrg
585c8548ba8Smrg        dispatch_async(dispatch_get_main_queue(), ^{
586c8548ba8Smrg            [X11App.controller set_window_menu:allMenuItems];
587c8548ba8Smrg        });
58835c4bbdfSmrg    }
5894642e01fSmrg}
5904642e01fSmrg
59135c4bbdfSmrgvoid
59235c4bbdfSmrgX11ApplicationSetWindowMenuCheck(int idx)
59335c4bbdfSmrg{
594c8548ba8Smrg    dispatch_async(dispatch_get_main_queue(), ^{
595c8548ba8Smrg        [X11App.controller set_window_menu_check:@(idx)];
596c8548ba8Smrg    });
5974642e01fSmrg}
5984642e01fSmrg
59935c4bbdfSmrgvoid
60035c4bbdfSmrgX11ApplicationSetFrontProcess(void)
60135c4bbdfSmrg{
602c8548ba8Smrg    dispatch_async(dispatch_get_main_queue(), ^{
603c8548ba8Smrg        [X11App set_front_process:nil];
604c8548ba8Smrg    });
6054642e01fSmrg}
6064642e01fSmrg
60735c4bbdfSmrgvoid
60835c4bbdfSmrgX11ApplicationSetCanQuit(int state)
60935c4bbdfSmrg{
610c8548ba8Smrg    dispatch_async(dispatch_get_main_queue(), ^{
611c8548ba8Smrg        X11App.controller.can_quit = !!state;
612c8548ba8Smrg    });
6134642e01fSmrg}
6144642e01fSmrg
61535c4bbdfSmrgvoid
61635c4bbdfSmrgX11ApplicationServerReady(void)
61735c4bbdfSmrg{
618c8548ba8Smrg    dispatch_async(dispatch_get_main_queue(), ^{
619c8548ba8Smrg        [X11App.controller server_ready];
620c8548ba8Smrg    });
6214642e01fSmrg}
6224642e01fSmrg
62335c4bbdfSmrgvoid
62435c4bbdfSmrgX11ApplicationShowHideMenubar(int state)
62535c4bbdfSmrg{
626c8548ba8Smrg    dispatch_async(dispatch_get_main_queue(), ^{
627c8548ba8Smrg        [X11App show_hide_menubar:@(state)];
628c8548ba8Smrg    });
6294642e01fSmrg}
6304642e01fSmrg
63135c4bbdfSmrgvoid
63235c4bbdfSmrgX11ApplicationLaunchClient(const char *cmd)
63335c4bbdfSmrg{
634c8548ba8Smrg    @autoreleasepool {
635c8548ba8Smrg        NSString *string = @(cmd);
636c8548ba8Smrg        dispatch_async(dispatch_get_main_queue(), ^{
637c8548ba8Smrg            [X11App launch_client:string];
638c8548ba8Smrg        });
639c8548ba8Smrg    }
6406747b715Smrg}
6416747b715Smrg
6428223e2f2Smrg/* This is a special function in that it is run from the *SERVER* thread and
6438223e2f2Smrg * not the AppKit thread.  We want to block entering a screen-capturing RandR
6448223e2f2Smrg * mode until we notify the user about how to get out if the X11 client crashes.
6458223e2f2Smrg */
64635c4bbdfSmrgBool
64735c4bbdfSmrgX11ApplicationCanEnterRandR(void)
64835c4bbdfSmrg{
6498223e2f2Smrg    NSString *title, *msg;
650dc61d50dSmrg    NSUserDefaults * const defaults = NSUserDefaults.xquartzDefaults;
65135c4bbdfSmrg
652dc61d50dSmrg    if ([defaults boolForKey:XQuartzPrefKeyNoRANDRAlert] ||
65335c4bbdfSmrg        XQuartzShieldingWindowLevel != 0)
6548223e2f2Smrg        return TRUE;
6558223e2f2Smrg
65635c4bbdfSmrg    title = NSLocalizedString(@"Enter RandR mode?",
65735c4bbdfSmrg                              @"Dialog title when switching to RandR");
65835c4bbdfSmrg    msg = NSLocalizedString(
65935c4bbdfSmrg        @"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.",
66035c4bbdfSmrg        @"Dialog when switching to RandR");
66135c4bbdfSmrg
66235c4bbdfSmrg    if (!XQuartzIsRootless)
6638223e2f2Smrg        QuartzShowFullscreen(FALSE);
6648223e2f2Smrg
665c8548ba8Smrg    NSInteger __block alert_result;
666c8548ba8Smrg    dispatch_sync(dispatch_get_main_queue(), ^{
667c8548ba8Smrg        alert_result = NSRunAlertPanel(title, @"%@",
668c8548ba8Smrg                                       NSLocalizedString(@"Allow", @""),
669c8548ba8Smrg                                       NSLocalizedString(@"Cancel", @""),
670c8548ba8Smrg                                       NSLocalizedString(@"Always Allow", @""), msg);
671c8548ba8Smrg    });
672c8548ba8Smrg
673c8548ba8Smrg    switch (alert_result) {
67435c4bbdfSmrg    case NSAlertOtherReturn:
675dc61d50dSmrg        [defaults setBool:YES forKey:XQuartzPrefKeyNoRANDRAlert];
67635c4bbdfSmrg
67735c4bbdfSmrg    case NSAlertDefaultReturn:
67835c4bbdfSmrg        return YES;
67935c4bbdfSmrg
68035c4bbdfSmrg    default:
68135c4bbdfSmrg        return NO;
6828223e2f2Smrg    }
6838223e2f2Smrg}
6848223e2f2Smrg
68535c4bbdfSmrgstatic void
68635c4bbdfSmrgcheck_xinitrc(void)
68735c4bbdfSmrg{
6884642e01fSmrg    char *tem, buf[1024];
6894642e01fSmrg    NSString *msg;
690dc61d50dSmrg    NSUserDefaults * const defaults = NSUserDefaults.xquartzDefaults;
69135c4bbdfSmrg
692dc61d50dSmrg    if ([defaults boolForKey:XQuartzPrefKeyDoneXinitCheck])
69335c4bbdfSmrg        return;
69435c4bbdfSmrg
69535c4bbdfSmrg    tem = getenv("HOME");
6964642e01fSmrg    if (tem == NULL) goto done;
69735c4bbdfSmrg
69835c4bbdfSmrg    snprintf(buf, sizeof(buf), "%s/.xinitrc", tem);
69935c4bbdfSmrg    if (access(buf, F_OK) != 0)
70035c4bbdfSmrg        goto done;
70135c4bbdfSmrg
70235c4bbdfSmrg    msg =
70335c4bbdfSmrg        NSLocalizedString(
70435c4bbdfSmrg            @"You have an existing ~/.xinitrc file.\n\n\
70535c4bbdfSmrg                             Windows displayed by X11 applications may not have titlebars, or may look \
70635c4bbdfSmrg                             different to windows displayed by native applications.\n\n\
70735c4bbdfSmrg                             Would you like to move aside the existing file and use the standard X11 \
70835c4bbdfSmrg                             environment the next time you start X11?"                                                                                                                                                                                                                                                                                                                                                                  ,
70935c4bbdfSmrg            @"Startup xinitrc dialog");
71035c4bbdfSmrg
71135c4bbdfSmrg    if (NSAlertDefaultReturn ==
71235c4bbdfSmrg        NSRunAlertPanel(nil, @"%@", NSLocalizedString(@"Yes", @""),
71335c4bbdfSmrg                        NSLocalizedString(@"No", @""), nil, msg)) {
7144642e01fSmrg        char buf2[1024];
7154642e01fSmrg        int i = -1;
7164642e01fSmrg
71735c4bbdfSmrg        snprintf(buf2, sizeof(buf2), "%s.old", buf);
71835c4bbdfSmrg
71935c4bbdfSmrg        for (i = 1; access(buf2, F_OK) == 0; i++)
72035c4bbdfSmrg            snprintf(buf2, sizeof(buf2), "%s.old.%d", buf, i);
72135c4bbdfSmrg
72235c4bbdfSmrg        rename(buf, buf2);
7234642e01fSmrg    }
72435c4bbdfSmrg
72535c4bbdfSmrgdone:
726dc61d50dSmrg    [defaults setBool:YES forKey:XQuartzPrefKeyDoneXinitCheck];
7274642e01fSmrg}
7284642e01fSmrg
72935c4bbdfSmrgstatic inline pthread_t
73035c4bbdfSmrgcreate_thread(void *(*func)(void *), void *arg)
73135c4bbdfSmrg{
7326747b715Smrg    pthread_attr_t attr;
7336747b715Smrg    pthread_t tid;
73435c4bbdfSmrg
7356747b715Smrg    pthread_attr_init(&attr);
7366747b715Smrg    pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
7376747b715Smrg    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
7386747b715Smrg    pthread_create(&tid, &attr, func, arg);
7396747b715Smrg    pthread_attr_destroy(&attr);
74035c4bbdfSmrg
7416747b715Smrg    return tid;
7426747b715Smrg}
7436747b715Smrg
74435c4bbdfSmrgstatic void *
74535c4bbdfSmrgxpbproxy_x_thread(void *args)
74635c4bbdfSmrg{
7476747b715Smrg    xpbproxy_run();
7486747b715Smrg
74935c4bbdfSmrg    ErrorF("xpbproxy thread is terminating unexpectedly.\n");
7506747b715Smrg    return NULL;
7516747b715Smrg}
7526747b715Smrg
75335c4bbdfSmrgvoid
75435c4bbdfSmrgX11ApplicationMain(int argc, char **argv, char **envp)
75535c4bbdfSmrg{
7564642e01fSmrg#ifdef DEBUG
75735c4bbdfSmrg    while (access("/tmp/x11-block", F_OK) == 0) sleep(1);
7584642e01fSmrg#endif
75935c4bbdfSmrg
760c8548ba8Smrg    @autoreleasepool {
761c8548ba8Smrg        X11App = (X11Application *)[X11Application sharedApplication];
762dc61d50dSmrg        [X11App read_defaults];
76335c4bbdfSmrg
764c8548ba8Smrg        [NSBundle loadNibNamed:@"main" owner:NSApp];
765c8548ba8Smrg        [NSNotificationCenter.defaultCenter addObserver:NSApp
766c8548ba8Smrg                                               selector:@selector (became_key:)
767c8548ba8Smrg                                                   name:NSWindowDidBecomeKeyNotification
768c8548ba8Smrg                                                 object:nil];
7694642e01fSmrg
770c8548ba8Smrg        /*
771c8548ba8Smrg         * The xpr Quartz mode is statically linked into this server.
772c8548ba8Smrg         * Initialize all the Quartz functions.
773c8548ba8Smrg         */
774c8548ba8Smrg        QuartzModeBundleInit();
7754642e01fSmrg
776c8548ba8Smrg        /* Calculate the height of the menubar so we can avoid it. */
777c8548ba8Smrg        aquaMenuBarHeight = NSApp.mainMenu.menuBarHeight;
778c8548ba8Smrg        if (!aquaMenuBarHeight) {
779c8548ba8Smrg            NSScreen* primaryScreen = NSScreen.screens[0];
780c8548ba8Smrg            aquaMenuBarHeight = NSHeight(primaryScreen.frame) - NSMaxY(primaryScreen.visibleFrame);
781c8548ba8Smrg        }
78235c4bbdfSmrg
783c8548ba8Smrg        eventTranslationQueue = dispatch_queue_create(BUNDLE_ID_PREFIX ".X11.NSEventsToX11EventsQueue", NULL);
784c8548ba8Smrg        assert(eventTranslationQueue != NULL);
7854642e01fSmrg
786c8548ba8Smrg        /* Set the key layout seed before we start the server */
787c8548ba8Smrg        last_key_layout = TISCopyCurrentKeyboardLayoutInputSource();
7884642e01fSmrg
789c8548ba8Smrg        if (!last_key_layout) {
790c8548ba8Smrg            ErrorF("X11ApplicationMain: Unable to determine TISCopyCurrentKeyboardLayoutInputSource() at startup.\n");
791c8548ba8Smrg        }
7924642e01fSmrg
793c8548ba8Smrg        if (!QuartsResyncKeymap(FALSE)) {
794c8548ba8Smrg            ErrorF("X11ApplicationMain: Could not build a valid keymap.\n");
795c8548ba8Smrg        }
7964642e01fSmrg
797c8548ba8Smrg        /* Tell the server thread that it can proceed */
798c8548ba8Smrg        QuartzInitServer(argc, argv, envp);
79935c4bbdfSmrg
800c8548ba8Smrg        /* This must be done after QuartzInitServer because it can result in
801c8548ba8Smrg         * an mieqEnqueue() - <rdar://problem/6300249>
802c8548ba8Smrg         */
803c8548ba8Smrg        check_xinitrc();
80435c4bbdfSmrg
805c8548ba8Smrg        create_thread(xpbproxy_x_thread, NULL);
8066747b715Smrg
8076747b715Smrg#if XQUARTZ_SPARKLE
808c8548ba8Smrg        [[X11App controller] setup_sparkle];
809c8548ba8Smrg        [[SUUpdater sharedUpdater] resetUpdateCycle];
810c8548ba8Smrg        //    [[SUUpdater sharedUpdater] checkForUpdates:X11App];
8116747b715Smrg#endif
812c8548ba8Smrg    }
8136747b715Smrg
8144642e01fSmrg    [NSApp run];
8154642e01fSmrg    /* not reached */
8164642e01fSmrg}
8174642e01fSmrg
8184642e01fSmrg@implementation X11Application (Private)
8194642e01fSmrg
8204642e01fSmrg#ifdef NX_DEVICELCMDKEYMASK
8214642e01fSmrg/* This is to workaround a bug in the VNC server where we sometimes see the L
8224642e01fSmrg * modifier and sometimes see no "side"
8234642e01fSmrg */
82435c4bbdfSmrgstatic inline int
82535c4bbdfSmrgensure_flag(int flags, int device_independent, int device_dependents,
82635c4bbdfSmrg            int device_dependent_default)
82735c4bbdfSmrg{
82835c4bbdfSmrg    if ((flags & device_independent) &&
82935c4bbdfSmrg        !(flags & device_dependents))
8304642e01fSmrg        flags |= device_dependent_default;
8314642e01fSmrg    return flags;
8324642e01fSmrg}
8334642e01fSmrg#endif
8344642e01fSmrg
8359ace9065Smrg#ifdef DEBUG_UNTRUSTED_POINTER_DELTA
83635c4bbdfSmrgstatic const char *
83735c4bbdfSmrguntrusted_str(NSEvent *e)
83835c4bbdfSmrg{
83935c4bbdfSmrg    switch ([e type]) {
84035c4bbdfSmrg    case NSScrollWheel:
84135c4bbdfSmrg        return "NSScrollWheel";
84235c4bbdfSmrg
84335c4bbdfSmrg    case NSTabletPoint:
84435c4bbdfSmrg        return "NSTabletPoint";
84535c4bbdfSmrg
84635c4bbdfSmrg    case NSOtherMouseDown:
84735c4bbdfSmrg        return "NSOtherMouseDown";
84835c4bbdfSmrg
84935c4bbdfSmrg    case NSOtherMouseUp:
85035c4bbdfSmrg        return "NSOtherMouseUp";
85135c4bbdfSmrg
85235c4bbdfSmrg    case NSLeftMouseDown:
85335c4bbdfSmrg        return "NSLeftMouseDown";
85435c4bbdfSmrg
85535c4bbdfSmrg    case NSLeftMouseUp:
85635c4bbdfSmrg        return "NSLeftMouseUp";
85735c4bbdfSmrg
85835c4bbdfSmrg    default:
85935c4bbdfSmrg        switch ([e subtype]) {
86035c4bbdfSmrg        case NSTabletPointEventSubtype:
86135c4bbdfSmrg            return "NSTabletPointEventSubtype";
86235c4bbdfSmrg
86335c4bbdfSmrg        case NSTabletProximityEventSubtype:
86435c4bbdfSmrg            return "NSTabletProximityEventSubtype";
86535c4bbdfSmrg
8669ace9065Smrg        default:
86735c4bbdfSmrg            return "Other";
86835c4bbdfSmrg        }
8699ace9065Smrg    }
8709ace9065Smrg}
8719ace9065Smrg#endif
8729ace9065Smrg
87335c4bbdfSmrgextern void
8741b5d61b8Smrgwait_for_mieq_init(void);
87535c4bbdfSmrg
87635c4bbdfSmrg- (void) sendX11NSEvent:(NSEvent *)e
87735c4bbdfSmrg{
87835c4bbdfSmrg    NSPoint location = NSZeroPoint;
8794642e01fSmrg    int ev_button, ev_type;
88035c4bbdfSmrg    static float pressure = 0.0;       // static so ProximityOut will have the value from the previous tablet event
88135c4bbdfSmrg    static NSPoint tilt;               // static so ProximityOut will have the value from the previous tablet event
88235c4bbdfSmrg    static DeviceIntPtr darwinTabletCurrent = NULL;
88335c4bbdfSmrg    static BOOL needsProximityIn = NO; // Do we do need to handle a pending ProximityIn once we have pressure/tilt?
8844642e01fSmrg    DeviceIntPtr pDev;
8854642e01fSmrg    int modifierFlags;
8866747b715Smrg    BOOL isMouseOrTabletEvent, isTabletEvent;
8874642e01fSmrg
88835c4bbdfSmrg    if (!darwinTabletCurrent) {
88935c4bbdfSmrg        /* Ensure that the event system is initialized */
8901b5d61b8Smrg        wait_for_mieq_init();
89135c4bbdfSmrg        assert(darwinTabletStylus);
89235c4bbdfSmrg
89335c4bbdfSmrg        tilt = NSZeroPoint;
89435c4bbdfSmrg        darwinTabletCurrent = darwinTabletStylus;
89535c4bbdfSmrg    }
89635c4bbdfSmrg
89735c4bbdfSmrg    isMouseOrTabletEvent = [e type] == NSLeftMouseDown ||
89835c4bbdfSmrg                           [e type] == NSOtherMouseDown ||
89935c4bbdfSmrg                           [e type] == NSRightMouseDown ||
90035c4bbdfSmrg                           [e type] == NSLeftMouseUp ||
90135c4bbdfSmrg                           [e type] == NSOtherMouseUp ||
90235c4bbdfSmrg                           [e type] == NSRightMouseUp ||
90335c4bbdfSmrg                           [e type] == NSLeftMouseDragged ||
90435c4bbdfSmrg                           [e type] == NSOtherMouseDragged ||
90535c4bbdfSmrg                           [e type] == NSRightMouseDragged ||
90635c4bbdfSmrg                           [e type] == NSMouseMoved ||
90735c4bbdfSmrg                           [e type] == NSTabletPoint || 
90835c4bbdfSmrg                           [e type] == NSScrollWheel;
9094642e01fSmrg
9106747b715Smrg    isTabletEvent = ([e type] == NSTabletPoint) ||
91135c4bbdfSmrg                    (isMouseOrTabletEvent &&
91235c4bbdfSmrg                     ([e subtype] == NSTabletPointEventSubtype ||
91335c4bbdfSmrg                      [e subtype] == NSTabletProximityEventSubtype));
9144642e01fSmrg
91535c4bbdfSmrg    if (isMouseOrTabletEvent) {
9166747b715Smrg        static NSPoint lastpt;
9176747b715Smrg        NSWindow *window = [e window];
9186747b715Smrg        NSRect screen = [[[NSScreen screens] objectAtIndex:0] frame];
91935c4bbdfSmrg        BOOL hasUntrustedPointerDelta;
92035c4bbdfSmrg
9216747b715Smrg        // NSEvents for tablets are not consistent wrt deltaXY between events, so we cannot rely on that
9226747b715Smrg        // Thus tablets will be subject to the warp-pointer bug worked around by the delta, but tablets
9236747b715Smrg        // are not normally used in cases where that bug would present itself, so this is a fair tradeoff
9246747b715Smrg        // <rdar://problem/7111003> deltaX and deltaY are incorrect for NSMouseMoved, NSTabletPointEventSubtype
9256747b715Smrg        // http://xquartz.macosforge.org/trac/ticket/288
9266747b715Smrg        hasUntrustedPointerDelta = isTabletEvent;
92735c4bbdfSmrg
9286747b715Smrg        // The deltaXY for middle click events also appear erroneous after fast user switching
9296747b715Smrg        // <rdar://problem/7979468> deltaX and deltaY are incorrect for NSOtherMouseDown and NSOtherMouseUp after FUS
9306747b715Smrg        // http://xquartz.macosforge.org/trac/ticket/389
93135c4bbdfSmrg        hasUntrustedPointerDelta |= [e type] == NSOtherMouseDown ||
93235c4bbdfSmrg                                    [e type] == NSOtherMouseUp;
9336747b715Smrg
9346747b715Smrg        // The deltaXY for scroll events correspond to the scroll delta, not the pointer delta
9356747b715Smrg        // <rdar://problem/7989690> deltaXY for wheel events are being sent as mouse movement
93635c4bbdfSmrg        hasUntrustedPointerDelta |= [e type] == NSScrollWheel;
9379ace9065Smrg
9389ace9065Smrg#ifdef DEBUG_UNTRUSTED_POINTER_DELTA
93935c4bbdfSmrg        hasUntrustedPointerDelta |= [e type] == NSLeftMouseDown ||
94035c4bbdfSmrg                                    [e type] == NSLeftMouseUp;
9419ace9065Smrg#endif
94235c4bbdfSmrg
94335c4bbdfSmrg        if (window != nil) {
9446747b715Smrg            NSRect frame = [window frame];
9456747b715Smrg            location = [e locationInWindow];
9466747b715Smrg            location.x += frame.origin.x;
9476747b715Smrg            location.y += frame.origin.y;
9486747b715Smrg            lastpt = location;
94935c4bbdfSmrg        }
95035c4bbdfSmrg        else if (hasUntrustedPointerDelta) {
9519ace9065Smrg#ifdef DEBUG_UNTRUSTED_POINTER_DELTA
9529ace9065Smrg            ErrorF("--- Begin Event Debug ---\n");
9539ace9065Smrg            ErrorF("Event type: %s\n", untrusted_str(e));
9549ace9065Smrg            ErrorF("old lastpt: (%0.2f, %0.2f)\n", lastpt.x, lastpt.y);
9559ace9065Smrg            ErrorF("     delta: (%0.2f, %0.2f)\n", [e deltaX], -[e deltaY]);
95635c4bbdfSmrg            ErrorF("  location: (%0.2f, %0.2f)\n", lastpt.x + [e deltaX],
95735c4bbdfSmrg                   lastpt.y - [e deltaY]);
95835c4bbdfSmrg            ErrorF("workaround: (%0.2f, %0.2f)\n", [e locationInWindow].x,
95935c4bbdfSmrg                   [e locationInWindow].y);
9609ace9065Smrg            ErrorF("--- End Event Debug ---\n");
9619ace9065Smrg
9629ace9065Smrg            location.x = lastpt.x + [e deltaX];
9639ace9065Smrg            location.y = lastpt.y - [e deltaY];
9649ace9065Smrg            lastpt = [e locationInWindow];
9659ace9065Smrg#else
9666747b715Smrg            location = [e locationInWindow];
9676747b715Smrg            lastpt = location;
9689ace9065Smrg#endif
96935c4bbdfSmrg        }
97035c4bbdfSmrg        else {
9716747b715Smrg            location.x = lastpt.x + [e deltaX];
9726747b715Smrg            location.y = lastpt.y - [e deltaY];
9736747b715Smrg            lastpt = [e locationInWindow];
9746747b715Smrg        }
97535c4bbdfSmrg
9766747b715Smrg        /* Convert coordinate system */
9776747b715Smrg        location.y = (screen.origin.y + screen.size.height) - location.y;
9786747b715Smrg    }
97935c4bbdfSmrg
9804642e01fSmrg    modifierFlags = [e modifierFlags];
98135c4bbdfSmrg
9824642e01fSmrg#ifdef NX_DEVICELCMDKEYMASK
9834642e01fSmrg    /* This is to workaround a bug in the VNC server where we sometimes see the L
9844642e01fSmrg     * modifier and sometimes see no "side"
9854642e01fSmrg     */
98635c4bbdfSmrg    modifierFlags = ensure_flag(modifierFlags, NX_CONTROLMASK,
98735c4bbdfSmrg                                NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK,
98835c4bbdfSmrg                                NX_DEVICELCTLKEYMASK);
98935c4bbdfSmrg    modifierFlags = ensure_flag(modifierFlags, NX_SHIFTMASK,
99035c4bbdfSmrg                                NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK, 
99135c4bbdfSmrg                                NX_DEVICELSHIFTKEYMASK);
99235c4bbdfSmrg    modifierFlags = ensure_flag(modifierFlags, NX_COMMANDMASK,
99335c4bbdfSmrg                                NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK,
99435c4bbdfSmrg                                NX_DEVICELCMDKEYMASK);
99535c4bbdfSmrg    modifierFlags = ensure_flag(modifierFlags, NX_ALTERNATEMASK,
99635c4bbdfSmrg                                NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK,
99735c4bbdfSmrg                                NX_DEVICELALTKEYMASK);
9984642e01fSmrg#endif
9994642e01fSmrg
10006747b715Smrg    modifierFlags &= darwin_all_modifier_mask;
10014642e01fSmrg
10024642e01fSmrg    /* We don't receive modifier key events while out of focus, and 3button
10034642e01fSmrg     * emulation mucks this up, so we need to check our modifier flag state
10044642e01fSmrg     * on every event... ugg
10054642e01fSmrg     */
100635c4bbdfSmrg
100735c4bbdfSmrg    if (darwin_all_modifier_flags != modifierFlags)
10084642e01fSmrg        DarwinUpdateModKeys(modifierFlags);
10094642e01fSmrg
101035c4bbdfSmrg    switch ([e type]) {
101135c4bbdfSmrg    case NSLeftMouseDown:
101235c4bbdfSmrg        ev_button = 1;
101335c4bbdfSmrg        ev_type = ButtonPress;
101435c4bbdfSmrg        goto handle_mouse;
101535c4bbdfSmrg
101635c4bbdfSmrg    case NSOtherMouseDown:
1017ed6184dfSmrg        // Get the AppKit button number, and convert it from 0-based to 1-based
1018ed6184dfSmrg        ev_button = [e buttonNumber] + 1;
1019ed6184dfSmrg
1020ed6184dfSmrg        /* Translate middle mouse button (3 in AppKit) to button 2 in X11,
1021ed6184dfSmrg         * and translate additional mouse buttons (4 and higher in AppKit)
1022ed6184dfSmrg         * to buttons 8 and higher in X11, to match default behavior of X11
1023ed6184dfSmrg         * on other platforms
1024ed6184dfSmrg         */
1025ed6184dfSmrg        ev_button = (ev_button == 3) ? 2 : (ev_button + 4);
1026ed6184dfSmrg
102735c4bbdfSmrg        ev_type = ButtonPress;
102835c4bbdfSmrg        goto handle_mouse;
102935c4bbdfSmrg
103035c4bbdfSmrg    case NSRightMouseDown:
103135c4bbdfSmrg        ev_button = 3;
103235c4bbdfSmrg        ev_type = ButtonPress;
103335c4bbdfSmrg        goto handle_mouse;
103435c4bbdfSmrg
103535c4bbdfSmrg    case NSLeftMouseUp:
103635c4bbdfSmrg        ev_button = 1;
103735c4bbdfSmrg        ev_type = ButtonRelease;
103835c4bbdfSmrg        goto handle_mouse;
103935c4bbdfSmrg
104035c4bbdfSmrg    case NSOtherMouseUp:
1041ed6184dfSmrg        // See above comments for NSOtherMouseDown
1042ed6184dfSmrg        ev_button = [e buttonNumber] + 1;
1043ed6184dfSmrg        ev_button = (ev_button == 3) ? 2 : (ev_button + 4);
104435c4bbdfSmrg        ev_type = ButtonRelease;
104535c4bbdfSmrg        goto handle_mouse;
104635c4bbdfSmrg
104735c4bbdfSmrg    case NSRightMouseUp:
104835c4bbdfSmrg        ev_button = 3;
104935c4bbdfSmrg        ev_type = ButtonRelease;
105035c4bbdfSmrg        goto handle_mouse;
105135c4bbdfSmrg
105235c4bbdfSmrg    case NSLeftMouseDragged:
105335c4bbdfSmrg        ev_button = 1;
105435c4bbdfSmrg        ev_type = MotionNotify;
105535c4bbdfSmrg        goto handle_mouse;
105635c4bbdfSmrg
105735c4bbdfSmrg    case NSOtherMouseDragged:
1058ed6184dfSmrg        // See above comments for NSOtherMouseDown
1059ed6184dfSmrg        ev_button = [e buttonNumber] + 1;
1060ed6184dfSmrg        ev_button = (ev_button == 3) ? 2 : (ev_button + 4);
106135c4bbdfSmrg        ev_type = MotionNotify;
106235c4bbdfSmrg        goto handle_mouse;
106335c4bbdfSmrg
106435c4bbdfSmrg    case NSRightMouseDragged:
106535c4bbdfSmrg        ev_button = 3;
106635c4bbdfSmrg        ev_type = MotionNotify;
106735c4bbdfSmrg        goto handle_mouse;
106835c4bbdfSmrg
106935c4bbdfSmrg    case NSMouseMoved:
107035c4bbdfSmrg        ev_button = 0;
107135c4bbdfSmrg        ev_type = MotionNotify;
107235c4bbdfSmrg        goto handle_mouse;
107335c4bbdfSmrg
107435c4bbdfSmrg    case NSTabletPoint:
107535c4bbdfSmrg        ev_button = 0;
107635c4bbdfSmrg        ev_type = MotionNotify;
107735c4bbdfSmrg        goto handle_mouse;
107835c4bbdfSmrg
107935c4bbdfSmrghandle_mouse:
108035c4bbdfSmrg        pDev = darwinPointer;
108135c4bbdfSmrg
108235c4bbdfSmrg        /* NSTabletPoint can have no subtype */
108335c4bbdfSmrg        if ([e type] != NSTabletPoint &&
108435c4bbdfSmrg            [e subtype] == NSTabletProximityEventSubtype) {
108535c4bbdfSmrg            switch ([e pointingDeviceType]) {
108635c4bbdfSmrg            case NSEraserPointingDevice:
108735c4bbdfSmrg                darwinTabletCurrent = darwinTabletEraser;
108835c4bbdfSmrg                break;
108935c4bbdfSmrg
109035c4bbdfSmrg            case NSPenPointingDevice:
109135c4bbdfSmrg                darwinTabletCurrent = darwinTabletStylus;
109235c4bbdfSmrg                break;
109335c4bbdfSmrg
109435c4bbdfSmrg            case NSCursorPointingDevice:
109535c4bbdfSmrg            case NSUnknownPointingDevice:
109635c4bbdfSmrg            default:
109735c4bbdfSmrg                darwinTabletCurrent = darwinTabletCursor;
109835c4bbdfSmrg                break;
10994642e01fSmrg            }
11004642e01fSmrg
110135c4bbdfSmrg            if ([e isEnteringProximity])
110235c4bbdfSmrg                needsProximityIn = YES;
110335c4bbdfSmrg            else
110435c4bbdfSmrg                DarwinSendTabletEvents(darwinTabletCurrent, ProximityOut, 0,
110535c4bbdfSmrg                                       location.x, location.y, pressure,
110635c4bbdfSmrg                                       tilt.x, tilt.y);
110735c4bbdfSmrg            return;
110835c4bbdfSmrg        }
110935c4bbdfSmrg
111035c4bbdfSmrg        if ([e type] == NSTabletPoint ||
111135c4bbdfSmrg            [e subtype] == NSTabletPointEventSubtype) {
111235c4bbdfSmrg            pressure = [e pressure];
111335c4bbdfSmrg            tilt = [e tilt];
111435c4bbdfSmrg
111535c4bbdfSmrg            pDev = darwinTabletCurrent;
111635c4bbdfSmrg
111735c4bbdfSmrg            if (needsProximityIn) {
111835c4bbdfSmrg                DarwinSendTabletEvents(darwinTabletCurrent, ProximityIn, 0,
111935c4bbdfSmrg                                       location.x, location.y, pressure,
112035c4bbdfSmrg                                       tilt.x, tilt.y);
112135c4bbdfSmrg
112235c4bbdfSmrg                needsProximityIn = NO;
11234642e01fSmrg            }
112435c4bbdfSmrg        }
11254642e01fSmrg
112635c4bbdfSmrg        if (!XQuartzServerVisible && noTestExtensions) {
112735c4bbdfSmrg            xp_window_id wid = 0;
112835c4bbdfSmrg            xp_error err;
112935c4bbdfSmrg
113035c4bbdfSmrg            /* Sigh. Need to check that we're really over one of
113135c4bbdfSmrg             * our windows. (We need to receive pointer events while
113235c4bbdfSmrg             * not in the foreground, but we don't want to receive them
113335c4bbdfSmrg             * when another window is over us or we might show a tooltip)
113435c4bbdfSmrg             */
11356747b715Smrg
113635c4bbdfSmrg            err = xp_find_window(location.x, location.y, 0, &wid);
11376747b715Smrg
113835c4bbdfSmrg            if (err != XP_Success || (err == XP_Success && wid == 0))
113935c4bbdfSmrg            {
114035c4bbdfSmrg                bgMouseLocation = location;
114135c4bbdfSmrg                bgMouseLocationUpdated = TRUE;
114235c4bbdfSmrg                return;
11436747b715Smrg            }
114435c4bbdfSmrg        }
114535c4bbdfSmrg
114635c4bbdfSmrg        if (bgMouseLocationUpdated) {
114735c4bbdfSmrg            if (!(ev_type == MotionNotify && ev_button == 0)) {
114835c4bbdfSmrg                DarwinSendPointerEvents(darwinPointer, MotionNotify, 0,
114935c4bbdfSmrg                                        location.x, location.y,
115035c4bbdfSmrg                                        0.0, 0.0);
11516747b715Smrg            }
115235c4bbdfSmrg            bgMouseLocationUpdated = FALSE;
115335c4bbdfSmrg        }
11546747b715Smrg
115535c4bbdfSmrg        if (pDev == darwinPointer) {
115635c4bbdfSmrg            DarwinSendPointerEvents(pDev, ev_type, ev_button,
115735c4bbdfSmrg                                    location.x, location.y,
115835c4bbdfSmrg                                    [e deltaX], [e deltaY]);
115935c4bbdfSmrg        } else {
116035c4bbdfSmrg            DarwinSendTabletEvents(pDev, ev_type, ev_button,
116135c4bbdfSmrg                                   location.x, location.y, pressure,
116235c4bbdfSmrg                                   tilt.x, tilt.y);
116335c4bbdfSmrg        }
116435c4bbdfSmrg
116535c4bbdfSmrg        break;
116635c4bbdfSmrg
116735c4bbdfSmrg    case NSTabletProximity:
116835c4bbdfSmrg        switch ([e pointingDeviceType]) {
116935c4bbdfSmrg        case NSEraserPointingDevice:
117035c4bbdfSmrg            darwinTabletCurrent = darwinTabletEraser;
11714642e01fSmrg            break;
117235c4bbdfSmrg
117335c4bbdfSmrg        case NSPenPointingDevice:
117435c4bbdfSmrg            darwinTabletCurrent = darwinTabletStylus;
117535c4bbdfSmrg            break;
117635c4bbdfSmrg
117735c4bbdfSmrg        case NSCursorPointingDevice:
117835c4bbdfSmrg        case NSUnknownPointingDevice:
117935c4bbdfSmrg        default:
118035c4bbdfSmrg            darwinTabletCurrent = darwinTabletCursor;
11814642e01fSmrg            break;
118235c4bbdfSmrg        }
118335c4bbdfSmrg
118435c4bbdfSmrg        if ([e isEnteringProximity])
118535c4bbdfSmrg            needsProximityIn = YES;
118635c4bbdfSmrg        else
118735c4bbdfSmrg            DarwinSendTabletEvents(darwinTabletCurrent, ProximityOut, 0,
118835c4bbdfSmrg                                   location.x, location.y, pressure,
118935c4bbdfSmrg                                   tilt.x, tilt.y);
119035c4bbdfSmrg        break;
119135c4bbdfSmrg
119235c4bbdfSmrg    case NSScrollWheel:
119335c4bbdfSmrg    {
119435c4bbdfSmrg        CGFloat deltaX = [e deltaX];
119535c4bbdfSmrg        CGFloat deltaY = [e deltaY];
119635c4bbdfSmrg        CGEventRef cge = [e CGEvent];
119735c4bbdfSmrg        BOOL isContinuous =
119835c4bbdfSmrg            CGEventGetIntegerValueField(cge, kCGScrollWheelEventIsContinuous);
119935c4bbdfSmrg
120035c4bbdfSmrg#if 0
120135c4bbdfSmrg        /* Scale the scroll value by line height */
120235c4bbdfSmrg        CGEventSourceRef source = CGEventCreateSourceFromEvent(cge);
120335c4bbdfSmrg        if (source) {
120435c4bbdfSmrg            double lineHeight = CGEventSourceGetPixelsPerLine(source);
120535c4bbdfSmrg            CFRelease(source);
12064642e01fSmrg            
120735c4bbdfSmrg            /* There's no real reason for the 1/5 ratio here other than that
120835c4bbdfSmrg             * it feels like a good ratio after some testing.
120935c4bbdfSmrg             */
121035c4bbdfSmrg            
121135c4bbdfSmrg            deltaX *= lineHeight / 5.0;
121235c4bbdfSmrg            deltaY *= lineHeight / 5.0;
121335c4bbdfSmrg        }
121435c4bbdfSmrg#endif
121535c4bbdfSmrg        
1216c8548ba8Smrg        if (XQuartzScrollInDeviceDirection &&
121735c4bbdfSmrg            [e isDirectionInvertedFromDevice]) {
121835c4bbdfSmrg            deltaX *= -1;
121935c4bbdfSmrg            deltaY *= -1;
122035c4bbdfSmrg        }
122135c4bbdfSmrg        /* This hack is in place to better deal with "clicky" scroll wheels:
122235c4bbdfSmrg         * http://xquartz.macosforge.org/trac/ticket/562
122335c4bbdfSmrg         */
122435c4bbdfSmrg        if (!isContinuous) {
122535c4bbdfSmrg            static NSTimeInterval lastScrollTime = 0.0;
122635c4bbdfSmrg
122735c4bbdfSmrg            /* These store how much extra we have already scrolled.
122835c4bbdfSmrg             * ie, this is how much we ignore on the next event.
122935c4bbdfSmrg             */
123035c4bbdfSmrg            static double deficit_x = 0.0;
123135c4bbdfSmrg            static double deficit_y = 0.0;
123235c4bbdfSmrg
123335c4bbdfSmrg            /* If we have past a second since the last scroll, wipe the slate
123435c4bbdfSmrg             * clean
123535c4bbdfSmrg             */
123635c4bbdfSmrg            if ([e timestamp] - lastScrollTime > 1.0) {
123735c4bbdfSmrg                deficit_x = deficit_y = 0.0;
1238475c125cSmrg            }
123935c4bbdfSmrg            lastScrollTime = [e timestamp];
124035c4bbdfSmrg
124135c4bbdfSmrg            if (deltaX != 0.0) {
124235c4bbdfSmrg                /* If we changed directions, wipe the slate clean */
124335c4bbdfSmrg                if ((deficit_x < 0.0 && deltaX > 0.0) ||
124435c4bbdfSmrg                    (deficit_x > 0.0 && deltaX < 0.0)) {
124535c4bbdfSmrg                    deficit_x = 0.0;
124635c4bbdfSmrg                }
124735c4bbdfSmrg
124835c4bbdfSmrg                /* Eat up the deficit, but ensure that something is
124935c4bbdfSmrg                 * always sent 
12506747b715Smrg                 */
125135c4bbdfSmrg                if (fabs(deltaX) > fabs(deficit_x)) {
125235c4bbdfSmrg                    deltaX -= deficit_x;
125335c4bbdfSmrg
125435c4bbdfSmrg                    if (deltaX > 0.0) {
125535c4bbdfSmrg                        deficit_x = ceil(deltaX) - deltaX;
125635c4bbdfSmrg                        deltaX = ceil(deltaX);
125735c4bbdfSmrg                    } else {
125835c4bbdfSmrg                        deficit_x = floor(deltaX) - deltaX;
125935c4bbdfSmrg                        deltaX = floor(deltaX);
126035c4bbdfSmrg                    }
126135c4bbdfSmrg                } else {
126235c4bbdfSmrg                    deficit_x -= deltaX;
126335c4bbdfSmrg
126435c4bbdfSmrg                    if (deltaX > 0.0) {
126535c4bbdfSmrg                        deltaX = 1.0;
126635c4bbdfSmrg                    } else {
126735c4bbdfSmrg                        deltaX = -1.0;
126835c4bbdfSmrg                    }
126935c4bbdfSmrg
127035c4bbdfSmrg                    deficit_x += deltaX;
12716747b715Smrg                }
12726747b715Smrg            }
12736747b715Smrg
127435c4bbdfSmrg            if (deltaY != 0.0) {
127535c4bbdfSmrg                /* If we changed directions, wipe the slate clean */
127635c4bbdfSmrg                if ((deficit_y < 0.0 && deltaY > 0.0) ||
127735c4bbdfSmrg                    (deficit_y > 0.0 && deltaY < 0.0)) {
127835c4bbdfSmrg                    deficit_y = 0.0;
127935c4bbdfSmrg                }
128035c4bbdfSmrg
128135c4bbdfSmrg                /* Eat up the deficit, but ensure that something is
128235c4bbdfSmrg                 * always sent 
128335c4bbdfSmrg                 */
128435c4bbdfSmrg                if (fabs(deltaY) > fabs(deficit_y)) {
128535c4bbdfSmrg                    deltaY -= deficit_y;
128635c4bbdfSmrg
128735c4bbdfSmrg                    if (deltaY > 0.0) {
128835c4bbdfSmrg                        deficit_y = ceil(deltaY) - deltaY;
128935c4bbdfSmrg                        deltaY = ceil(deltaY);
129035c4bbdfSmrg                    } else {
129135c4bbdfSmrg                        deficit_y = floor(deltaY) - deltaY;
129235c4bbdfSmrg                        deltaY = floor(deltaY);
129335c4bbdfSmrg                    }
12944642e01fSmrg                } else {
129535c4bbdfSmrg                    deficit_y -= deltaY;
129635c4bbdfSmrg
129735c4bbdfSmrg                    if (deltaY > 0.0) {
129835c4bbdfSmrg                        deltaY = 1.0;
129935c4bbdfSmrg                    } else {
130035c4bbdfSmrg                        deltaY = -1.0;
130135c4bbdfSmrg                    }
130235c4bbdfSmrg
130335c4bbdfSmrg                    deficit_y += deltaY;
130435c4bbdfSmrg                }
130535c4bbdfSmrg            }
130635c4bbdfSmrg        }
130735c4bbdfSmrg
130835c4bbdfSmrg        DarwinSendScrollEvents(deltaX, deltaY);
130935c4bbdfSmrg        break;
131035c4bbdfSmrg    }
131135c4bbdfSmrg
131235c4bbdfSmrg    case NSKeyDown:
131335c4bbdfSmrg    case NSKeyUp:
131435c4bbdfSmrg    {
131535c4bbdfSmrg        /* XKB clobbers our keymap at startup, so we need to force it on the first keypress.
131635c4bbdfSmrg         * TODO: Make this less of a kludge.
131735c4bbdfSmrg         */
131835c4bbdfSmrg        static int force_resync_keymap = YES;
131935c4bbdfSmrg        if (force_resync_keymap) {
132035c4bbdfSmrg            DarwinSendDDXEvent(kXquartzReloadKeymap, 0);
132135c4bbdfSmrg            force_resync_keymap = NO;
132235c4bbdfSmrg        }
132335c4bbdfSmrg    }
132435c4bbdfSmrg
132535c4bbdfSmrg        if (darwinSyncKeymap) {
1326dc61d50dSmrg            __block TISInputSourceRef key_layout;
1327dc61d50dSmrg            dispatch_block_t copyCurrentKeyboardLayoutInputSource = ^{
1328dc61d50dSmrg                key_layout = TISCopyCurrentKeyboardLayoutInputSource();
1329dc61d50dSmrg            };
1330dc61d50dSmrg            /* This is an ugly ant-pattern, but it is more expedient to address the problem right now. */
1331dc61d50dSmrg            if (pthread_main_np()) {
1332dc61d50dSmrg                copyCurrentKeyboardLayoutInputSource();
1333dc61d50dSmrg            } else {
1334dc61d50dSmrg                dispatch_sync(dispatch_get_main_queue(), copyCurrentKeyboardLayoutInputSource);
1335dc61d50dSmrg            }
1336dc61d50dSmrg
133735c4bbdfSmrg            TISInputSourceRef clear;
133835c4bbdfSmrg            if (CFEqual(key_layout, last_key_layout)) {
133935c4bbdfSmrg                CFRelease(key_layout);
134035c4bbdfSmrg            }
134135c4bbdfSmrg            else {
134235c4bbdfSmrg                /* Swap/free thread-safely */
134335c4bbdfSmrg                clear = last_key_layout;
134435c4bbdfSmrg                last_key_layout = key_layout;
134535c4bbdfSmrg                CFRelease(clear);
1346c8548ba8Smrg
134735c4bbdfSmrg                /* Update keyInfo */
134835c4bbdfSmrg                if (!QuartsResyncKeymap(TRUE)) {
134935c4bbdfSmrg                    ErrorF(
135035c4bbdfSmrg                        "sendX11NSEvent: Could not build a valid keymap.\n");
13514642e01fSmrg                }
13524642e01fSmrg            }
135335c4bbdfSmrg        }
13544642e01fSmrg
135535c4bbdfSmrg        ev_type = ([e type] == NSKeyDown) ? KeyPress : KeyRelease;
135635c4bbdfSmrg        DarwinSendKeyboardEvents(ev_type, [e keyCode]);
135735c4bbdfSmrg        break;
13584642e01fSmrg
135935c4bbdfSmrg    default:
136035c4bbdfSmrg        break;              /* for gcc */
136135c4bbdfSmrg    }
13624642e01fSmrg}
13634642e01fSmrg@end
1364