xserver

xserver with xephyr scale patch
git clone https://git.neptards.moe/u3shit/xserver.git
Log | Files | Refs | README | LICENSE

X11Application.m (56271B)


      1 /* X11Application.m -- subclass of NSApplication to multiplex events
      2  *
      3  * Copyright (c) 2002-2012 Apple Inc. All rights reserved.
      4  *
      5  * Permission is hereby granted, free of charge, to any person
      6  * obtaining a copy of this software and associated documentation files
      7  * (the "Software"), to deal in the Software without restriction,
      8  * including without limitation the rights to use, copy, modify, merge,
      9  * publish, distribute, sublicense, and/or sell copies of the Software,
     10  * and to permit persons to whom the Software is furnished to do so,
     11  * subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be
     14  * included in all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     19  * NONINFRINGEMENT.  IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
     20  * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     21  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     23  * DEALINGS IN THE SOFTWARE.
     24  *
     25  * Except as contained in this notice, the name(s) of the above
     26  * copyright holders shall not be used in advertising or otherwise to
     27  * promote the sale, use or other dealings in this Software without
     28  * prior written authorization.
     29  */
     30 
     31 #include "sanitizedCarbon.h"
     32 
     33 #ifdef HAVE_DIX_CONFIG_H
     34 #include <dix-config.h>
     35 #endif
     36 
     37 #import "X11Application.h"
     38 
     39 #include "darwin.h"
     40 #include "quartz.h"
     41 #include "darwinEvents.h"
     42 #include "quartzKeyboard.h"
     43 #include <X11/extensions/applewmconst.h>
     44 #include "micmap.h"
     45 #include "exglobals.h"
     46 
     47 #include <mach/mach.h>
     48 #include <unistd.h>
     49 
     50 #include <pthread.h>
     51 
     52 #include <Xplugin.h>
     53 
     54 // pbproxy/pbproxy.h
     55 extern int
     56 xpbproxy_run(void);
     57 
     58 #define DEFAULTS_FILE X11LIBDIR "/X11/xserver/Xquartz.plist"
     59 
     60 #ifndef XSERVER_VERSION
     61 #define XSERVER_VERSION "?"
     62 #endif
     63 
     64 #include <dispatch/dispatch.h>
     65 
     66 static dispatch_queue_t eventTranslationQueue;
     67 
     68 #ifndef __has_feature
     69 #define __has_feature(x) 0
     70 #endif
     71 
     72 #ifndef CF_RETURNS_RETAINED
     73 #if __has_feature(attribute_cf_returns_retained)
     74 #define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
     75 #else
     76 #define CF_RETURNS_RETAINED
     77 #endif
     78 #endif
     79 
     80 #ifndef APPKIT_APPFLAGS_HACK
     81 #define APPKIT_APPFLAGS_HACK 1
     82 #endif
     83 
     84 extern Bool noTestExtensions;
     85 extern Bool noRenderExtension;
     86 
     87 static TISInputSourceRef last_key_layout;
     88 
     89 /* This preference is only tested on Lion or later as it's not relevant to
     90  * earlier OS versions.
     91  */
     92 Bool XQuartzScrollInDeviceDirection = FALSE;
     93 
     94 extern int darwinFakeButtons;
     95 
     96 /* Store the mouse location while in the background, and update X11's pointer
     97  * location when we become the foreground application
     98  */
     99 static NSPoint bgMouseLocation;
    100 static BOOL bgMouseLocationUpdated = FALSE;
    101 
    102 X11Application *X11App;
    103 
    104 CFStringRef app_prefs_domain_cfstr = NULL;
    105 
    106 #define ALL_KEY_MASKS (NSShiftKeyMask | NSControlKeyMask | \
    107                        NSAlternateKeyMask | NSCommandKeyMask)
    108 
    109 #if APPKIT_APPFLAGS_HACK && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101500
    110 // This was removed from the SDK in 10.15
    111 @interface NSApplication () {
    112 @protected
    113     /* All instance variables are private */
    114     short               _running;
    115     struct __appFlags {
    116         unsigned int        _hidden:1;
    117         unsigned int        _appleEventActivationInProgress:1;
    118         unsigned int        _active:1;
    119         unsigned int        _hasBeenRun:1;
    120         unsigned int        _doingUnhide:1;
    121         unsigned int        _delegateReturnsValidRequestor:1;
    122         unsigned int        _deactPending:1;
    123         unsigned int        _invalidState:1;
    124         unsigned int        _invalidEvent:1;
    125         unsigned int        _postedWindowsNeedUpdateNote:1;
    126         unsigned int        _wantsToActivate:1;
    127         unsigned int        _doingHide:1;
    128         unsigned int        _dontSendShouldTerminate:1;
    129         unsigned int        _ignoresFullScreen:1;
    130         unsigned int        _finishedLaunching:1;
    131         unsigned int        _hasEventDelegate:1;
    132         unsigned int        _appTerminating:1;
    133         unsigned int        _didNSOpenOrPrint:1;
    134         unsigned int        _inDealloc:1;
    135         unsigned int        _pendingDidFinish:1;
    136         unsigned int        _hasKeyFocus:1;
    137         unsigned int        _panelsNonactivating:1;
    138         unsigned int        _hiddenOnLaunch:1;
    139         unsigned int        _openStatus:2;
    140         unsigned int        _batchOrdering:1;
    141         unsigned int        _waitingForTerminationReply:1;
    142         unsigned int        _unused:1;
    143         unsigned int        _enumeratingMemoryPressureHandlers:1;
    144         unsigned int        _didTryRestoringPersistentState:1;
    145         unsigned int        _windowDragging:1;
    146         unsigned int        _mightBeSwitching:1;
    147     }                   _appFlags;
    148     id                  _mainMenu;
    149 }
    150 @end
    151 #endif
    152 
    153 @interface NSApplication (Internal)
    154 - (void)_setKeyWindow:(id)window;
    155 - (void)_setMainWindow:(id)window;
    156 @end
    157 
    158 @interface X11Application (Private)
    159 - (void) sendX11NSEvent:(NSEvent *)e;
    160 @end
    161 
    162 @interface X11Application ()
    163 @property (nonatomic, readwrite, assign) OSX_BOOL x_active;
    164 @end
    165 
    166 @implementation X11Application
    167 
    168 typedef struct message_struct message;
    169 struct message_struct {
    170     mach_msg_header_t hdr;
    171     SEL selector;
    172     NSObject *arg;
    173 };
    174 
    175 /* Quartz mode initialization routine. This is often dynamically loaded
    176    but is statically linked into this X server. */
    177 Bool
    178 QuartzModeBundleInit(void);
    179 
    180 - (void) dealloc
    181 {
    182     self.controller = nil;
    183     [super dealloc];
    184 }
    185 
    186 - (void) orderFrontStandardAboutPanel: (id) sender
    187 {
    188     NSMutableDictionary *dict;
    189     NSDictionary *infoDict;
    190     NSString *tem;
    191 
    192     dict = [NSMutableDictionary dictionaryWithCapacity:3];
    193     infoDict = [[NSBundle mainBundle] infoDictionary];
    194 
    195     [dict setObject: NSLocalizedString(@"The X Window System", @"About panel")
    196              forKey:@"ApplicationName"];
    197 
    198     tem = [infoDict objectForKey:@"CFBundleShortVersionString"];
    199 
    200     [dict setObject:[NSString stringWithFormat:@"XQuartz %@", tem]
    201              forKey:@"ApplicationVersion"];
    202 
    203     [dict setObject:[NSString stringWithFormat:@"xorg-server %s",
    204                      XSERVER_VERSION]
    205      forKey:@"Version"];
    206 
    207     [self orderFrontStandardAboutPanelWithOptions: dict];
    208 }
    209 
    210 - (void) activateX:(OSX_BOOL)state
    211 {
    212     OSX_BOOL const x_active = self.x_active;
    213 
    214     if (x_active == state)
    215         return;
    216 
    217     DEBUG_LOG("state=%d, x_active=%d, \n", state, x_active);
    218     if (state) {
    219         if (bgMouseLocationUpdated) {
    220             DarwinSendPointerEvents(darwinPointer, MotionNotify, 0,
    221                                     bgMouseLocation.x, bgMouseLocation.y,
    222                                     0.0, 0.0);
    223             bgMouseLocationUpdated = FALSE;
    224         }
    225         DarwinSendDDXEvent(kXquartzActivate, 0);
    226     }
    227     else {
    228 
    229         if (darwin_all_modifier_flags)
    230             DarwinUpdateModKeys(0);
    231 
    232         DarwinInputReleaseButtonsAndKeys(darwinKeyboard);
    233         DarwinInputReleaseButtonsAndKeys(darwinPointer);
    234         DarwinInputReleaseButtonsAndKeys(darwinTabletCursor);
    235         DarwinInputReleaseButtonsAndKeys(darwinTabletStylus);
    236         DarwinInputReleaseButtonsAndKeys(darwinTabletEraser);
    237 
    238         DarwinSendDDXEvent(kXquartzDeactivate, 0);
    239     }
    240 
    241     self.x_active = state;
    242 }
    243 
    244 - (void) became_key:(NSWindow *)win
    245 {
    246     [self activateX:NO];
    247 }
    248 
    249 - (void) sendEvent:(NSEvent *)e
    250 {
    251     /* Don't try sending to X if we haven't initialized.  This can happen if AppKit takes over
    252      * (eg: uncaught exception) early in launch.
    253      */
    254     if (!eventTranslationQueue) {
    255         [super sendEvent:e];
    256         return;
    257     }
    258 
    259     OSX_BOOL for_appkit, for_x;
    260     OSX_BOOL const x_active = self.x_active;
    261 
    262     /* By default pass down the responder chain and to X. */
    263     for_appkit = YES;
    264     for_x = YES;
    265 
    266     switch ([e type]) {
    267     case NSLeftMouseDown:
    268     case NSRightMouseDown:
    269     case NSOtherMouseDown:
    270     case NSLeftMouseUp:
    271     case NSRightMouseUp:
    272     case NSOtherMouseUp:
    273         if ([e window] != nil) {
    274             /* Pointer event has an (AppKit) window. Probably something for the kit. */
    275             for_x = NO;
    276             if (x_active) [self activateX:NO];
    277         }
    278         else if ([self modalWindow] == nil) {
    279             /* Must be an X window. Tell appkit windows to resign main/key */
    280             for_appkit = NO;
    281 
    282             if (!x_active && quartzProcs->IsX11Window([e windowNumber])) {
    283                 if ([self respondsToSelector:@selector(_setKeyWindow:)] && [self respondsToSelector:@selector(_setMainWindow:)]) {
    284                     NSWindow *keyWindow = [self keyWindow];
    285                     if (keyWindow) {
    286                         [self _setKeyWindow:nil];
    287                         [keyWindow resignKeyWindow];
    288                     }
    289 
    290                     NSWindow *mainWindow = [self mainWindow];
    291                     if (mainWindow) {
    292                         [self _setMainWindow:nil];
    293                         [mainWindow resignMainWindow];
    294                    }
    295                  } else {
    296                     /* This has a side effect of causing background apps to steal focus from XQuartz.
    297                      * Unfortunately, there is no public and stable API to do what we want, but this
    298                      * is a decent fallback in the off chance that the above selectors get dropped
    299                      * in the future.
    300                      */
    301                     [self deactivate];
    302                 }
    303 
    304                 [self activateX:YES];
    305             }
    306         }
    307 
    308         /* We want to force sending to appkit if we're over the menu bar */
    309         if (!for_appkit) {
    310             NSPoint NSlocation = [e locationInWindow];
    311             NSWindow *window = [e window];
    312             NSRect NSframe, NSvisibleFrame;
    313             CGRect CGframe, CGvisibleFrame;
    314             CGPoint CGlocation;
    315 
    316             if (window != nil) {
    317                 NSRect frame = [window frame];
    318                 NSlocation.x += frame.origin.x;
    319                 NSlocation.y += frame.origin.y;
    320             }
    321 
    322             NSframe = [[NSScreen mainScreen] frame];
    323             NSvisibleFrame = [[NSScreen mainScreen] visibleFrame];
    324 
    325             CGframe = CGRectMake(NSframe.origin.x, NSframe.origin.y,
    326                                  NSframe.size.width, NSframe.size.height);
    327             CGvisibleFrame = CGRectMake(NSvisibleFrame.origin.x,
    328                                         NSvisibleFrame.origin.y,
    329                                         NSvisibleFrame.size.width,
    330                                         NSvisibleFrame.size.height);
    331             CGlocation = CGPointMake(NSlocation.x, NSlocation.y);
    332 
    333             if (CGRectContainsPoint(CGframe, CGlocation) &&
    334                 !CGRectContainsPoint(CGvisibleFrame, CGlocation))
    335                 for_appkit = YES;
    336         }
    337 
    338         break;
    339 
    340     case NSKeyDown:
    341     case NSKeyUp:
    342 
    343         if (_x_active) {
    344             static BOOL do_swallow = NO;
    345             static int swallow_keycode;
    346 
    347             if ([e type] == NSKeyDown) {
    348                 /* Before that though, see if there are any global
    349                  * shortcuts bound to it. */
    350 
    351                 if (darwinAppKitModMask &[e modifierFlags]) {
    352                     /* Override to force sending to Appkit */
    353                     swallow_keycode = [e keyCode];
    354                     do_swallow = YES;
    355                     for_x = NO;
    356                 } else if (XQuartzEnableKeyEquivalents &&
    357                          xp_is_symbolic_hotkey_event([e eventRef])) {
    358                     swallow_keycode = [e keyCode];
    359                     do_swallow = YES;
    360                     for_x = NO;
    361                 }
    362                 else if (XQuartzEnableKeyEquivalents &&
    363                          [[self mainMenu] performKeyEquivalent:e]) {
    364                     swallow_keycode = [e keyCode];
    365                     do_swallow = YES;
    366                     for_appkit = NO;
    367                     for_x = NO;
    368                 }
    369                 else if (!XQuartzIsRootless
    370                          && ([e modifierFlags] & ALL_KEY_MASKS) ==
    371                          (NSCommandKeyMask | NSAlternateKeyMask)
    372                          && ([e keyCode] == 0 /*a*/ || [e keyCode] ==
    373                              53 /*Esc*/)) {
    374                     /* We have this here to force processing fullscreen
    375                      * toggle even if XQuartzEnableKeyEquivalents is disabled */
    376                     swallow_keycode = [e keyCode];
    377                     do_swallow = YES;
    378                     for_x = NO;
    379                     for_appkit = NO;
    380                     DarwinSendDDXEvent(kXquartzToggleFullscreen, 0);
    381                 }
    382                 else {
    383                     /* No kit window is focused, so send it to X. */
    384                     for_appkit = NO;
    385 
    386                     /* Reset our swallow state if we're seeing the same keyCode again.
    387                      * This can happen if we become !_x_active when the keyCode we
    388                      * intended to swallow is delivered.  See:
    389                      * https://bugs.freedesktop.org/show_bug.cgi?id=92648
    390                      */
    391                     if ([e keyCode] == swallow_keycode) {
    392                         do_swallow = NO;
    393                     }
    394                 }
    395             }
    396             else {       /* KeyUp */
    397                 /* If we saw a key equivalent on the down, don't pass
    398                  * the up through to X. */
    399                 if (do_swallow && [e keyCode] == swallow_keycode) {
    400                     do_swallow = NO;
    401                     for_x = NO;
    402                 }
    403             }
    404         }
    405         else {       /* !_x_active */
    406             for_x = NO;
    407         }
    408         break;
    409 
    410     case NSFlagsChanged:
    411         /* Don't tell X11 about modifiers changing while it's not active */
    412         if (!_x_active)
    413             for_x = NO;
    414         break;
    415 
    416     case NSAppKitDefined:
    417         switch ([e subtype]) {
    418             static BOOL x_was_active = NO;
    419 
    420         case NSApplicationActivatedEventType:
    421             for_x = NO;
    422             if ([e window] == nil && x_was_active) {
    423                 BOOL order_all_windows = YES, workspaces, ok;
    424                 for_appkit = NO;
    425 
    426 #if APPKIT_APPFLAGS_HACK
    427                 /* FIXME: This is a hack to avoid passing the event to AppKit which
    428                  *        would result in it raising one of its windows.
    429                  */
    430                 _appFlags._active = YES;
    431 #endif
    432 
    433                 [self set_front_process:nil];
    434 
    435                 /* Get the Spaces preference for SwitchOnActivate */
    436                 (void)CFPreferencesAppSynchronize(CFSTR("com.apple.dock"));
    437                 workspaces =
    438                     CFPreferencesGetAppBooleanValue(CFSTR("workspaces"),
    439                                                     CFSTR(
    440                                                         "com.apple.dock"),
    441                                                     &ok);
    442                 if (!ok)
    443                     workspaces = NO;
    444 
    445                 if (workspaces) {
    446                     (void)CFPreferencesAppSynchronize(CFSTR(
    447                                                           ".GlobalPreferences"));
    448                     order_all_windows =
    449                         CFPreferencesGetAppBooleanValue(CFSTR(
    450                                                             "AppleSpacesSwitchOnActivate"),
    451                                                         CFSTR(
    452                                                             ".GlobalPreferences"),
    453                                                         &ok);
    454                     if (!ok)
    455                         order_all_windows = YES;
    456                 }
    457 
    458                 /* TODO: In the workspaces && !AppleSpacesSwitchOnActivate case, the windows are ordered
    459                  *       correctly, but we need to activate the top window on this space if there is
    460                  *       none active.
    461                  *
    462                  *       If there are no active windows, and there are minimized windows, we should
    463                  *       be restoring one of them.
    464                  */
    465                 if ([e data2] & 0x10) {         // 0x10 (bfCPSOrderAllWindowsForward) is set when we use cmd-tab or the dock icon
    466                     DarwinSendDDXEvent(kXquartzBringAllToFront, 1,
    467                                        order_all_windows);
    468                 }
    469             }
    470             break;
    471 
    472         case 18:         /* ApplicationDidReactivate */
    473             if (XQuartzFullscreenVisible) for_appkit = NO;
    474             break;
    475 
    476         case NSApplicationDeactivatedEventType:
    477             for_x = NO;
    478 
    479             x_was_active = _x_active;
    480             if (_x_active)
    481                 [self activateX:NO];
    482             break;
    483         }
    484         break;
    485 
    486     default:
    487         break;          /* for gcc */
    488     }
    489 
    490     if (for_appkit) [super sendEvent:e];
    491 
    492     if (for_x) {
    493         dispatch_async(eventTranslationQueue, ^{
    494                            [self sendX11NSEvent:e];
    495                        });
    496     }
    497 }
    498 
    499 - (void) set_apps_menu:(NSArray *)list
    500 {
    501     [self.controller set_apps_menu:list];
    502 }
    503 
    504 - (void) set_front_process:unused
    505 {
    506     [NSApp activateIgnoringOtherApps:YES];
    507 
    508     if ([self modalWindow] == nil)
    509         [self activateX:YES];
    510 }
    511 
    512 - (void) show_hide_menubar:(NSNumber *)state
    513 {
    514     /* Also shows/hides the dock */
    515     if ([state boolValue])
    516         SetSystemUIMode(kUIModeNormal, 0);
    517     else
    518         SetSystemUIMode(kUIModeAllHidden,
    519                         XQuartzFullscreenMenu ? kUIOptionAutoShowMenuBar : 0);                   // kUIModeAllSuppressed or kUIOptionAutoShowMenuBar can be used to allow "mouse-activation"
    520 }
    521 
    522 - (void) launch_client:(NSString *)cmd
    523 {
    524     (void)[self.controller application:self openFile:cmd];
    525 }
    526 
    527 /* user preferences */
    528 
    529 /* Note that these functions only work for arrays whose elements
    530    can be toll-free-bridged between NS and CF worlds. */
    531 
    532 static const void *
    533 cfretain(CFAllocatorRef a, const void *b)
    534 {
    535     return CFRetain(b);
    536 }
    537 
    538 static void
    539 cfrelease(CFAllocatorRef a, const void *b)
    540 {
    541     CFRelease(b);
    542 }
    543 
    544 CF_RETURNS_RETAINED
    545 static CFMutableArrayRef
    546 nsarray_to_cfarray(NSArray *in)
    547 {
    548     CFMutableArrayRef out;
    549     CFArrayCallBacks cb;
    550     NSObject *ns;
    551     const CFTypeRef *cf;
    552     int i, count;
    553 
    554     memset(&cb, 0, sizeof(cb));
    555     cb.version = 0;
    556     cb.retain = cfretain;
    557     cb.release = cfrelease;
    558 
    559     count = [in count];
    560     out = CFArrayCreateMutable(NULL, count, &cb);
    561 
    562     for (i = 0; i < count; i++) {
    563         ns = [in objectAtIndex:i];
    564 
    565         if ([ns isKindOfClass:[NSArray class]])
    566             cf = (CFTypeRef)nsarray_to_cfarray((NSArray *)ns);
    567         else
    568             cf = CFRetain((CFTypeRef)ns);
    569 
    570         CFArrayAppendValue(out, cf);
    571         CFRelease(cf);
    572     }
    573 
    574     return out;
    575 }
    576 
    577 static NSMutableArray *
    578 cfarray_to_nsarray(CFArrayRef in)
    579 {
    580     NSMutableArray *out;
    581     const CFTypeRef *cf;
    582     NSObject *ns;
    583     int i, count;
    584 
    585     count = CFArrayGetCount(in);
    586     out = [[NSMutableArray alloc] initWithCapacity:count];
    587 
    588     for (i = 0; i < count; i++) {
    589         cf = CFArrayGetValueAtIndex(in, i);
    590 
    591         if (CFGetTypeID(cf) == CFArrayGetTypeID())
    592             ns = cfarray_to_nsarray((CFArrayRef)cf);
    593         else
    594             ns = [(id) cf retain];
    595 
    596         [out addObject:ns];
    597         [ns release];
    598     }
    599 
    600     return out;
    601 }
    602 
    603 - (CFPropertyListRef) prefs_get_copy:(NSString *)key
    604 {
    605     CFPropertyListRef value;
    606 
    607     value = CFPreferencesCopyAppValue((CFStringRef)key,
    608                                       app_prefs_domain_cfstr);
    609 
    610     if (value == NULL) {
    611         static CFDictionaryRef defaults;
    612 
    613         if (defaults == NULL) {
    614             CFStringRef error = NULL;
    615             CFDataRef data;
    616             CFURLRef url;
    617             SInt32 error_code;
    618 
    619             url = (CFURLCreateFromFileSystemRepresentation
    620                        (NULL, (unsigned char *)DEFAULTS_FILE,
    621                        strlen(DEFAULTS_FILE), false));
    622             if (CFURLCreateDataAndPropertiesFromResource(NULL, url, &data,
    623                                                          NULL, NULL,
    624                                                          &error_code)) {
    625                 defaults = (CFPropertyListCreateFromXMLData
    626                                 (NULL, data,
    627                                 kCFPropertyListMutableContainersAndLeaves,
    628                                 &error));
    629                 if (error != NULL) CFRelease(error);
    630                 CFRelease(data);
    631             }
    632             CFRelease(url);
    633 
    634             if (defaults != NULL) {
    635                 NSMutableArray *apps, *elt;
    636                 int count, i;
    637                 NSString *name, *nname;
    638 
    639                 /* Localize the names in the default apps menu. */
    640 
    641                 apps =
    642                     [(NSDictionary *) defaults objectForKey:@PREFS_APPSMENU];
    643                 if (apps != nil) {
    644                     count = [apps count];
    645                     for (i = 0; i < count; i++) {
    646                         elt = [apps objectAtIndex:i];
    647                         if (elt != nil &&
    648                             [elt isKindOfClass:[NSArray class]]) {
    649                             name = [elt objectAtIndex:0];
    650                             if (name != nil) {
    651                                 nname = NSLocalizedString(name, nil);
    652                                 if (nname != nil && nname != name)
    653                                     [elt replaceObjectAtIndex:0 withObject:
    654                                      nname];
    655                             }
    656                         }
    657                     }
    658                 }
    659             }
    660         }
    661 
    662         if (defaults != NULL) value = CFDictionaryGetValue(defaults, key);
    663         if (value != NULL) CFRetain(value);
    664     }
    665 
    666     return value;
    667 }
    668 
    669 - (int) prefs_get_integer:(NSString *)key default:(int)def
    670 {
    671     CFPropertyListRef value;
    672     int ret;
    673 
    674     value = [self prefs_get_copy:key];
    675 
    676     if (value != NULL && CFGetTypeID(value) == CFNumberGetTypeID())
    677         CFNumberGetValue(value, kCFNumberIntType, &ret);
    678     else if (value != NULL && CFGetTypeID(value) == CFStringGetTypeID())
    679         ret = CFStringGetIntValue(value);
    680     else
    681         ret = def;
    682 
    683     if (value != NULL) CFRelease(value);
    684 
    685     return ret;
    686 }
    687 
    688 - (const char *) prefs_get_string:(NSString *)key default:(const char *)def
    689 {
    690     CFPropertyListRef value;
    691     const char *ret = NULL;
    692 
    693     value = [self prefs_get_copy:key];
    694 
    695     if (value != NULL && CFGetTypeID(value) == CFStringGetTypeID()) {
    696         NSString *s = (NSString *)value;
    697 
    698         ret = [s UTF8String];
    699     }
    700 
    701     if (value != NULL) CFRelease(value);
    702 
    703     return ret != NULL ? ret : def;
    704 }
    705 
    706 - (NSURL *) prefs_copy_url:(NSString *)key default:(NSURL *)def
    707 {
    708     CFPropertyListRef value;
    709     NSURL *ret = NULL;
    710 
    711     value = [self prefs_get_copy:key];
    712 
    713     if (value != NULL && CFGetTypeID(value) == CFStringGetTypeID()) {
    714         NSString *s = (NSString *)value;
    715 
    716         ret = [NSURL URLWithString:s];
    717         [ret retain];
    718     }
    719 
    720     if (value != NULL) CFRelease(value);
    721 
    722     return ret != NULL ? ret : def;
    723 }
    724 
    725 - (float) prefs_get_float:(NSString *)key default:(float)def
    726 {
    727     CFPropertyListRef value;
    728     float ret = def;
    729 
    730     value = [self prefs_get_copy:key];
    731 
    732     if (value != NULL
    733         && CFGetTypeID(value) == CFNumberGetTypeID()
    734         && CFNumberIsFloatType(value))
    735         CFNumberGetValue(value, kCFNumberFloatType, &ret);
    736     else if (value != NULL && CFGetTypeID(value) == CFStringGetTypeID())
    737         ret = CFStringGetDoubleValue(value);
    738 
    739     if (value != NULL) CFRelease(value);
    740 
    741     return ret;
    742 }
    743 
    744 - (int) prefs_get_boolean:(NSString *)key default:(int)def
    745 {
    746     CFPropertyListRef value;
    747     int ret = def;
    748 
    749     value = [self prefs_get_copy:key];
    750 
    751     if (value != NULL) {
    752         if (CFGetTypeID(value) == CFNumberGetTypeID())
    753             CFNumberGetValue(value, kCFNumberIntType, &ret);
    754         else if (CFGetTypeID(value) == CFBooleanGetTypeID())
    755             ret = CFBooleanGetValue(value);
    756         else if (CFGetTypeID(value) == CFStringGetTypeID()) {
    757             const char *tem = [(NSString *) value UTF8String];
    758             if (strcasecmp(tem, "true") == 0 || strcasecmp(tem, "yes") == 0)
    759                 ret = YES;
    760             else
    761                 ret = NO;
    762         }
    763 
    764         CFRelease(value);
    765     }
    766     return ret;
    767 }
    768 
    769 - (NSArray *) prefs_get_array:(NSString *)key
    770 {
    771     NSArray *ret = nil;
    772     CFPropertyListRef value;
    773 
    774     value = [self prefs_get_copy:key];
    775 
    776     if (value != NULL) {
    777         if (CFGetTypeID(value) == CFArrayGetTypeID())
    778             ret = [cfarray_to_nsarray (value)autorelease];
    779 
    780         CFRelease(value);
    781     }
    782 
    783     return ret;
    784 }
    785 
    786 - (void) prefs_set_integer:(NSString *)key value:(int)value
    787 {
    788     CFNumberRef x;
    789 
    790     x = CFNumberCreate(NULL, kCFNumberIntType, &value);
    791 
    792     CFPreferencesSetValue((CFStringRef)key, (CFTypeRef)x,
    793                           app_prefs_domain_cfstr,
    794                           kCFPreferencesCurrentUser,
    795                           kCFPreferencesAnyHost);
    796 
    797     CFRelease(x);
    798 }
    799 
    800 - (void) prefs_set_float:(NSString *)key value:(float)value
    801 {
    802     CFNumberRef x;
    803 
    804     x = CFNumberCreate(NULL, kCFNumberFloatType, &value);
    805 
    806     CFPreferencesSetValue((CFStringRef)key, (CFTypeRef)x,
    807                           app_prefs_domain_cfstr,
    808                           kCFPreferencesCurrentUser,
    809                           kCFPreferencesAnyHost);
    810 
    811     CFRelease(x);
    812 }
    813 
    814 - (void) prefs_set_boolean:(NSString *)key value:(int)value
    815 {
    816     CFPreferencesSetValue(
    817         (CFStringRef)key,
    818         (CFTypeRef)(value ? kCFBooleanTrue
    819                     : kCFBooleanFalse),
    820         app_prefs_domain_cfstr,
    821         kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
    822 
    823 }
    824 
    825 - (void) prefs_set_array:(NSString *)key value:(NSArray *)value
    826 {
    827     CFArrayRef cfarray;
    828 
    829     cfarray = nsarray_to_cfarray(value);
    830     CFPreferencesSetValue((CFStringRef)key,
    831                           (CFTypeRef)cfarray,
    832                           app_prefs_domain_cfstr,
    833                           kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
    834     CFRelease(cfarray);
    835 }
    836 
    837 - (void) prefs_set_string:(NSString *)key value:(NSString *)value
    838 {
    839     CFPreferencesSetValue((CFStringRef)key, (CFTypeRef)value,
    840                           app_prefs_domain_cfstr, kCFPreferencesCurrentUser,
    841                           kCFPreferencesAnyHost);
    842 }
    843 
    844 - (void) prefs_synchronize
    845 {
    846     CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
    847 }
    848 
    849 - (void) read_defaults
    850 {
    851     NSString *nsstr;
    852     const char *tem;
    853 
    854     XQuartzRootlessDefault = [self prefs_get_boolean:@PREFS_ROOTLESS
    855                               default               :XQuartzRootlessDefault];
    856     XQuartzFullscreenMenu = [self prefs_get_boolean:@PREFS_FULLSCREEN_MENU
    857                              default               :XQuartzFullscreenMenu];
    858     XQuartzFullscreenDisableHotkeys =
    859         ![self prefs_get_boolean:@PREFS_FULLSCREEN_HOTKEYS
    860           default               :!
    861           XQuartzFullscreenDisableHotkeys];
    862     darwinFakeButtons = [self prefs_get_boolean:@PREFS_FAKEBUTTONS
    863                          default               :darwinFakeButtons];
    864     XQuartzOptionSendsAlt = [self prefs_get_boolean:@PREFS_OPTION_SENDS_ALT
    865                              default               :XQuartzOptionSendsAlt];
    866 
    867     if (darwinFakeButtons) {
    868         const char *fake2, *fake3;
    869 
    870         fake2 = [self prefs_get_string:@PREFS_FAKE_BUTTON2 default:NULL];
    871         fake3 = [self prefs_get_string:@PREFS_FAKE_BUTTON3 default:NULL];
    872 
    873         if (fake2 != NULL) darwinFakeMouse2Mask = DarwinParseModifierList(
    874                 fake2, TRUE);
    875         if (fake3 != NULL) darwinFakeMouse3Mask = DarwinParseModifierList(
    876                 fake3, TRUE);
    877     }
    878 
    879     tem = [self prefs_get_string:@PREFS_APPKIT_MODIFIERS default:NULL];
    880     if (tem != NULL) darwinAppKitModMask = DarwinParseModifierList(tem, TRUE);
    881 
    882     tem = [self prefs_get_string:@PREFS_WINDOW_ITEM_MODIFIERS default:NULL];
    883     if (tem != NULL) {
    884         windowItemModMask = DarwinParseModifierList(tem, FALSE);
    885     }
    886     else {
    887         nsstr = NSLocalizedString(@"window item modifiers",
    888                                   @"window item modifiers");
    889         if (nsstr != NULL) {
    890             tem = [nsstr UTF8String];
    891             if ((tem != NULL) && strcmp(tem, "window item modifiers")) {
    892                 windowItemModMask = DarwinParseModifierList(tem, FALSE);
    893             }
    894         }
    895     }
    896 
    897     XQuartzEnableKeyEquivalents = [self prefs_get_boolean:@PREFS_KEYEQUIVS
    898                                    default               :
    899                                    XQuartzEnableKeyEquivalents];
    900 
    901     darwinSyncKeymap = [self prefs_get_boolean:@PREFS_SYNC_KEYMAP
    902                         default               :darwinSyncKeymap];
    903 
    904     darwinDesiredDepth = [self prefs_get_integer:@PREFS_DEPTH
    905                           default               :darwinDesiredDepth];
    906 
    907     noTestExtensions = ![self prefs_get_boolean:@PREFS_TEST_EXTENSIONS
    908                          default               :FALSE];
    909 
    910     noRenderExtension = ![self prefs_get_boolean:@PREFS_RENDER_EXTENSION
    911                           default               :TRUE];
    912 
    913     XQuartzScrollInDeviceDirection =
    914         [self prefs_get_boolean:@PREFS_SCROLL_IN_DEV_DIRECTION
    915          default               :
    916          XQuartzScrollInDeviceDirection];
    917 
    918 #if XQUARTZ_SPARKLE
    919     NSURL *url = [self prefs_copy_url:@PREFS_UPDATE_FEED default:nil];
    920     if (url) {
    921         [[SUUpdater sharedUpdater] setFeedURL:url];
    922         [url release];
    923     }
    924 #endif
    925 }
    926 
    927 /* This will end up at the end of the responder chain. */
    928 - (void) copy:sender
    929 {
    930     DarwinSendDDXEvent(kXquartzPasteboardNotify, 1,
    931                        AppleWMCopyToPasteboard);
    932 }
    933 
    934 @end
    935 
    936 void
    937 X11ApplicationSetWindowMenu(int nitems, const char **items,
    938                             const char *shortcuts)
    939 {
    940     @autoreleasepool {
    941         NSMutableArray <NSArray <NSString *> *> * const allMenuItems = [NSMutableArray array];
    942 
    943         for (int i = 0; i < nitems; i++) {
    944             NSMutableArray <NSString *> * const menuItem = [NSMutableArray array];
    945             [menuItem addObject:@(items[i])];
    946 
    947             if (shortcuts[i] == 0) {
    948                 [menuItem addObject:@""];
    949             } else {
    950                 [menuItem addObject:[NSString stringWithFormat:@"%d", shortcuts[i]]];
    951             }
    952 
    953             [allMenuItems addObject:menuItem];
    954         }
    955 
    956         dispatch_async(dispatch_get_main_queue(), ^{
    957             [X11App.controller set_window_menu:allMenuItems];
    958         });
    959     }
    960 }
    961 
    962 void
    963 X11ApplicationSetWindowMenuCheck(int idx)
    964 {
    965     dispatch_async(dispatch_get_main_queue(), ^{
    966         [X11App.controller set_window_menu_check:@(idx)];
    967     });
    968 }
    969 
    970 void
    971 X11ApplicationSetFrontProcess(void)
    972 {
    973     dispatch_async(dispatch_get_main_queue(), ^{
    974         [X11App set_front_process:nil];
    975     });
    976 }
    977 
    978 void
    979 X11ApplicationSetCanQuit(int state)
    980 {
    981     dispatch_async(dispatch_get_main_queue(), ^{
    982         X11App.controller.can_quit = !!state;
    983     });
    984 }
    985 
    986 void
    987 X11ApplicationServerReady(void)
    988 {
    989     dispatch_async(dispatch_get_main_queue(), ^{
    990         [X11App.controller server_ready];
    991     });
    992 }
    993 
    994 void
    995 X11ApplicationShowHideMenubar(int state)
    996 {
    997     dispatch_async(dispatch_get_main_queue(), ^{
    998         [X11App show_hide_menubar:@(state)];
    999     });
   1000 }
   1001 
   1002 void
   1003 X11ApplicationLaunchClient(const char *cmd)
   1004 {
   1005     @autoreleasepool {
   1006         NSString *string = @(cmd);
   1007         dispatch_async(dispatch_get_main_queue(), ^{
   1008             [X11App launch_client:string];
   1009         });
   1010     }
   1011 }
   1012 
   1013 /* This is a special function in that it is run from the *SERVER* thread and
   1014  * not the AppKit thread.  We want to block entering a screen-capturing RandR
   1015  * mode until we notify the user about how to get out if the X11 client crashes.
   1016  */
   1017 Bool
   1018 X11ApplicationCanEnterRandR(void)
   1019 {
   1020     NSString *title, *msg;
   1021 
   1022     if ([X11App prefs_get_boolean:@PREFS_NO_RANDR_ALERT default:NO] ||
   1023         XQuartzShieldingWindowLevel != 0)
   1024         return TRUE;
   1025 
   1026     title = NSLocalizedString(@"Enter RandR mode?",
   1027                               @"Dialog title when switching to RandR");
   1028     msg = NSLocalizedString(
   1029         @"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.",
   1030         @"Dialog when switching to RandR");
   1031 
   1032     if (!XQuartzIsRootless)
   1033         QuartzShowFullscreen(FALSE);
   1034 
   1035     NSInteger __block alert_result;
   1036     dispatch_sync(dispatch_get_main_queue(), ^{
   1037         alert_result = NSRunAlertPanel(title, @"%@",
   1038                                        NSLocalizedString(@"Allow", @""),
   1039                                        NSLocalizedString(@"Cancel", @""),
   1040                                        NSLocalizedString(@"Always Allow", @""), msg);
   1041     });
   1042 
   1043     switch (alert_result) {
   1044     case NSAlertOtherReturn:
   1045         [X11App prefs_set_boolean:@PREFS_NO_RANDR_ALERT value:YES];
   1046         [X11App prefs_synchronize];
   1047 
   1048     case NSAlertDefaultReturn:
   1049         return YES;
   1050 
   1051     default:
   1052         return NO;
   1053     }
   1054 }
   1055 
   1056 static void
   1057 check_xinitrc(void)
   1058 {
   1059     char *tem, buf[1024];
   1060     NSString *msg;
   1061 
   1062     if ([X11App prefs_get_boolean:@PREFS_DONE_XINIT_CHECK default:NO])
   1063         return;
   1064 
   1065     tem = getenv("HOME");
   1066     if (tem == NULL) goto done;
   1067 
   1068     snprintf(buf, sizeof(buf), "%s/.xinitrc", tem);
   1069     if (access(buf, F_OK) != 0)
   1070         goto done;
   1071 
   1072     msg =
   1073         NSLocalizedString(
   1074             @"You have an existing ~/.xinitrc file.\n\n\
   1075                              Windows displayed by X11 applications may not have titlebars, or may look \
   1076                              different to windows displayed by native applications.\n\n\
   1077                              Would you like to move aside the existing file and use the standard X11 \
   1078                              environment the next time you start X11?"                                                                                                                                                                                                                                                                                                                                                                  ,
   1079             @"Startup xinitrc dialog");
   1080 
   1081     if (NSAlertDefaultReturn ==
   1082         NSRunAlertPanel(nil, @"%@", NSLocalizedString(@"Yes", @""),
   1083                         NSLocalizedString(@"No", @""), nil, msg)) {
   1084         char buf2[1024];
   1085         int i = -1;
   1086 
   1087         snprintf(buf2, sizeof(buf2), "%s.old", buf);
   1088 
   1089         for (i = 1; access(buf2, F_OK) == 0; i++)
   1090             snprintf(buf2, sizeof(buf2), "%s.old.%d", buf, i);
   1091 
   1092         rename(buf, buf2);
   1093     }
   1094 
   1095 done:
   1096     [X11App prefs_set_boolean:@PREFS_DONE_XINIT_CHECK value:YES];
   1097     [X11App prefs_synchronize];
   1098 }
   1099 
   1100 static inline pthread_t
   1101 create_thread(void *(*func)(void *), void *arg)
   1102 {
   1103     pthread_attr_t attr;
   1104     pthread_t tid;
   1105 
   1106     pthread_attr_init(&attr);
   1107     pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
   1108     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
   1109     pthread_create(&tid, &attr, func, arg);
   1110     pthread_attr_destroy(&attr);
   1111 
   1112     return tid;
   1113 }
   1114 
   1115 static void *
   1116 xpbproxy_x_thread(void *args)
   1117 {
   1118     xpbproxy_run();
   1119 
   1120     ErrorF("xpbproxy thread is terminating unexpectedly.\n");
   1121     return NULL;
   1122 }
   1123 
   1124 void
   1125 X11ApplicationMain(int argc, char **argv, char **envp)
   1126 {
   1127 #ifdef DEBUG
   1128     while (access("/tmp/x11-block", F_OK) == 0) sleep(1);
   1129 #endif
   1130 
   1131     @autoreleasepool {
   1132         X11App = (X11Application *)[X11Application sharedApplication];
   1133 
   1134         app_prefs_domain_cfstr = (CFStringRef)[[NSBundle mainBundle] bundleIdentifier];
   1135 
   1136         if (app_prefs_domain_cfstr == NULL) {
   1137             ErrorF("X11ApplicationMain: Unable to determine bundle identifier.  Your installation of XQuartz may be broken.\n");
   1138             app_prefs_domain_cfstr = CFSTR(BUNDLE_ID_PREFIX ".X11");
   1139         }
   1140 
   1141         [NSApp read_defaults];
   1142         [NSBundle loadNibNamed:@"main" owner:NSApp];
   1143         [NSNotificationCenter.defaultCenter addObserver:NSApp
   1144                                                selector:@selector (became_key:)
   1145                                                    name:NSWindowDidBecomeKeyNotification
   1146                                                  object:nil];
   1147 
   1148         /*
   1149          * The xpr Quartz mode is statically linked into this server.
   1150          * Initialize all the Quartz functions.
   1151          */
   1152         QuartzModeBundleInit();
   1153 
   1154         /* Calculate the height of the menubar so we can avoid it. */
   1155         aquaMenuBarHeight = NSApp.mainMenu.menuBarHeight;
   1156         if (!aquaMenuBarHeight) {
   1157             NSScreen* primaryScreen = NSScreen.screens[0];
   1158             aquaMenuBarHeight = NSHeight(primaryScreen.frame) - NSMaxY(primaryScreen.visibleFrame);
   1159         }
   1160 
   1161         eventTranslationQueue = dispatch_queue_create(BUNDLE_ID_PREFIX ".X11.NSEventsToX11EventsQueue", NULL);
   1162         assert(eventTranslationQueue != NULL);
   1163 
   1164         /* Set the key layout seed before we start the server */
   1165         last_key_layout = TISCopyCurrentKeyboardLayoutInputSource();
   1166 
   1167         if (!last_key_layout) {
   1168             ErrorF("X11ApplicationMain: Unable to determine TISCopyCurrentKeyboardLayoutInputSource() at startup.\n");
   1169         }
   1170 
   1171         if (!QuartsResyncKeymap(FALSE)) {
   1172             ErrorF("X11ApplicationMain: Could not build a valid keymap.\n");
   1173         }
   1174 
   1175         /* Tell the server thread that it can proceed */
   1176         QuartzInitServer(argc, argv, envp);
   1177 
   1178         /* This must be done after QuartzInitServer because it can result in
   1179          * an mieqEnqueue() - <rdar://problem/6300249>
   1180          */
   1181         check_xinitrc();
   1182 
   1183         create_thread(xpbproxy_x_thread, NULL);
   1184 
   1185 #if XQUARTZ_SPARKLE
   1186         [[X11App controller] setup_sparkle];
   1187         [[SUUpdater sharedUpdater] resetUpdateCycle];
   1188         //    [[SUUpdater sharedUpdater] checkForUpdates:X11App];
   1189 #endif
   1190     }
   1191 
   1192     [NSApp run];
   1193     /* not reached */
   1194 }
   1195 
   1196 @implementation X11Application (Private)
   1197 
   1198 #ifdef NX_DEVICELCMDKEYMASK
   1199 /* This is to workaround a bug in the VNC server where we sometimes see the L
   1200  * modifier and sometimes see no "side"
   1201  */
   1202 static inline int
   1203 ensure_flag(int flags, int device_independent, int device_dependents,
   1204             int device_dependent_default)
   1205 {
   1206     if ((flags & device_independent) &&
   1207         !(flags & device_dependents))
   1208         flags |= device_dependent_default;
   1209     return flags;
   1210 }
   1211 #endif
   1212 
   1213 #ifdef DEBUG_UNTRUSTED_POINTER_DELTA
   1214 static const char *
   1215 untrusted_str(NSEvent *e)
   1216 {
   1217     switch ([e type]) {
   1218     case NSScrollWheel:
   1219         return "NSScrollWheel";
   1220 
   1221     case NSTabletPoint:
   1222         return "NSTabletPoint";
   1223 
   1224     case NSOtherMouseDown:
   1225         return "NSOtherMouseDown";
   1226 
   1227     case NSOtherMouseUp:
   1228         return "NSOtherMouseUp";
   1229 
   1230     case NSLeftMouseDown:
   1231         return "NSLeftMouseDown";
   1232 
   1233     case NSLeftMouseUp:
   1234         return "NSLeftMouseUp";
   1235 
   1236     default:
   1237         switch ([e subtype]) {
   1238         case NSTabletPointEventSubtype:
   1239             return "NSTabletPointEventSubtype";
   1240 
   1241         case NSTabletProximityEventSubtype:
   1242             return "NSTabletProximityEventSubtype";
   1243 
   1244         default:
   1245             return "Other";
   1246         }
   1247     }
   1248 }
   1249 #endif
   1250 
   1251 extern void
   1252 wait_for_mieq_init(void);
   1253 
   1254 - (void) sendX11NSEvent:(NSEvent *)e
   1255 {
   1256     NSPoint location = NSZeroPoint;
   1257     int ev_button, ev_type;
   1258     static float pressure = 0.0;       // static so ProximityOut will have the value from the previous tablet event
   1259     static NSPoint tilt;               // static so ProximityOut will have the value from the previous tablet event
   1260     static DeviceIntPtr darwinTabletCurrent = NULL;
   1261     static BOOL needsProximityIn = NO; // Do we do need to handle a pending ProximityIn once we have pressure/tilt?
   1262     DeviceIntPtr pDev;
   1263     int modifierFlags;
   1264     BOOL isMouseOrTabletEvent, isTabletEvent;
   1265 
   1266     if (!darwinTabletCurrent) {
   1267         /* Ensure that the event system is initialized */
   1268         wait_for_mieq_init();
   1269         assert(darwinTabletStylus);
   1270 
   1271         tilt = NSZeroPoint;
   1272         darwinTabletCurrent = darwinTabletStylus;
   1273     }
   1274 
   1275     isMouseOrTabletEvent = [e type] == NSLeftMouseDown ||
   1276                            [e type] == NSOtherMouseDown ||
   1277                            [e type] == NSRightMouseDown ||
   1278                            [e type] == NSLeftMouseUp ||
   1279                            [e type] == NSOtherMouseUp ||
   1280                            [e type] == NSRightMouseUp ||
   1281                            [e type] == NSLeftMouseDragged ||
   1282                            [e type] == NSOtherMouseDragged ||
   1283                            [e type] == NSRightMouseDragged ||
   1284                            [e type] == NSMouseMoved ||
   1285                            [e type] == NSTabletPoint || 
   1286                            [e type] == NSScrollWheel;
   1287 
   1288     isTabletEvent = ([e type] == NSTabletPoint) ||
   1289                     (isMouseOrTabletEvent &&
   1290                      ([e subtype] == NSTabletPointEventSubtype ||
   1291                       [e subtype] == NSTabletProximityEventSubtype));
   1292 
   1293     if (isMouseOrTabletEvent) {
   1294         static NSPoint lastpt;
   1295         NSWindow *window = [e window];
   1296         NSRect screen = [[[NSScreen screens] objectAtIndex:0] frame];
   1297         BOOL hasUntrustedPointerDelta;
   1298 
   1299         // NSEvents for tablets are not consistent wrt deltaXY between events, so we cannot rely on that
   1300         // Thus tablets will be subject to the warp-pointer bug worked around by the delta, but tablets
   1301         // are not normally used in cases where that bug would present itself, so this is a fair tradeoff
   1302         // <rdar://problem/7111003> deltaX and deltaY are incorrect for NSMouseMoved, NSTabletPointEventSubtype
   1303         // http://xquartz.macosforge.org/trac/ticket/288
   1304         hasUntrustedPointerDelta = isTabletEvent;
   1305 
   1306         // The deltaXY for middle click events also appear erroneous after fast user switching
   1307         // <rdar://problem/7979468> deltaX and deltaY are incorrect for NSOtherMouseDown and NSOtherMouseUp after FUS
   1308         // http://xquartz.macosforge.org/trac/ticket/389
   1309         hasUntrustedPointerDelta |= [e type] == NSOtherMouseDown ||
   1310                                     [e type] == NSOtherMouseUp;
   1311 
   1312         // The deltaXY for scroll events correspond to the scroll delta, not the pointer delta
   1313         // <rdar://problem/7989690> deltaXY for wheel events are being sent as mouse movement
   1314         hasUntrustedPointerDelta |= [e type] == NSScrollWheel;
   1315 
   1316 #ifdef DEBUG_UNTRUSTED_POINTER_DELTA
   1317         hasUntrustedPointerDelta |= [e type] == NSLeftMouseDown ||
   1318                                     [e type] == NSLeftMouseUp;
   1319 #endif
   1320 
   1321         if (window != nil) {
   1322             NSRect frame = [window frame];
   1323             location = [e locationInWindow];
   1324             location.x += frame.origin.x;
   1325             location.y += frame.origin.y;
   1326             lastpt = location;
   1327         }
   1328         else if (hasUntrustedPointerDelta) {
   1329 #ifdef DEBUG_UNTRUSTED_POINTER_DELTA
   1330             ErrorF("--- Begin Event Debug ---\n");
   1331             ErrorF("Event type: %s\n", untrusted_str(e));
   1332             ErrorF("old lastpt: (%0.2f, %0.2f)\n", lastpt.x, lastpt.y);
   1333             ErrorF("     delta: (%0.2f, %0.2f)\n", [e deltaX], -[e deltaY]);
   1334             ErrorF("  location: (%0.2f, %0.2f)\n", lastpt.x + [e deltaX],
   1335                    lastpt.y - [e deltaY]);
   1336             ErrorF("workaround: (%0.2f, %0.2f)\n", [e locationInWindow].x,
   1337                    [e locationInWindow].y);
   1338             ErrorF("--- End Event Debug ---\n");
   1339 
   1340             location.x = lastpt.x + [e deltaX];
   1341             location.y = lastpt.y - [e deltaY];
   1342             lastpt = [e locationInWindow];
   1343 #else
   1344             location = [e locationInWindow];
   1345             lastpt = location;
   1346 #endif
   1347         }
   1348         else {
   1349             location.x = lastpt.x + [e deltaX];
   1350             location.y = lastpt.y - [e deltaY];
   1351             lastpt = [e locationInWindow];
   1352         }
   1353 
   1354         /* Convert coordinate system */
   1355         location.y = (screen.origin.y + screen.size.height) - location.y;
   1356     }
   1357 
   1358     modifierFlags = [e modifierFlags];
   1359 
   1360 #ifdef NX_DEVICELCMDKEYMASK
   1361     /* This is to workaround a bug in the VNC server where we sometimes see the L
   1362      * modifier and sometimes see no "side"
   1363      */
   1364     modifierFlags = ensure_flag(modifierFlags, NX_CONTROLMASK,
   1365                                 NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK,
   1366                                 NX_DEVICELCTLKEYMASK);
   1367     modifierFlags = ensure_flag(modifierFlags, NX_SHIFTMASK,
   1368                                 NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK, 
   1369                                 NX_DEVICELSHIFTKEYMASK);
   1370     modifierFlags = ensure_flag(modifierFlags, NX_COMMANDMASK,
   1371                                 NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK,
   1372                                 NX_DEVICELCMDKEYMASK);
   1373     modifierFlags = ensure_flag(modifierFlags, NX_ALTERNATEMASK,
   1374                                 NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK,
   1375                                 NX_DEVICELALTKEYMASK);
   1376 #endif
   1377 
   1378     modifierFlags &= darwin_all_modifier_mask;
   1379 
   1380     /* We don't receive modifier key events while out of focus, and 3button
   1381      * emulation mucks this up, so we need to check our modifier flag state
   1382      * on every event... ugg
   1383      */
   1384 
   1385     if (darwin_all_modifier_flags != modifierFlags)
   1386         DarwinUpdateModKeys(modifierFlags);
   1387 
   1388     switch ([e type]) {
   1389     case NSLeftMouseDown:
   1390         ev_button = 1;
   1391         ev_type = ButtonPress;
   1392         goto handle_mouse;
   1393 
   1394     case NSOtherMouseDown:
   1395         // Get the AppKit button number, and convert it from 0-based to 1-based
   1396         ev_button = [e buttonNumber] + 1;
   1397 
   1398         /* Translate middle mouse button (3 in AppKit) to button 2 in X11,
   1399          * and translate additional mouse buttons (4 and higher in AppKit)
   1400          * to buttons 8 and higher in X11, to match default behavior of X11
   1401          * on other platforms
   1402          */
   1403         ev_button = (ev_button == 3) ? 2 : (ev_button + 4);
   1404 
   1405         ev_type = ButtonPress;
   1406         goto handle_mouse;
   1407 
   1408     case NSRightMouseDown:
   1409         ev_button = 3;
   1410         ev_type = ButtonPress;
   1411         goto handle_mouse;
   1412 
   1413     case NSLeftMouseUp:
   1414         ev_button = 1;
   1415         ev_type = ButtonRelease;
   1416         goto handle_mouse;
   1417 
   1418     case NSOtherMouseUp:
   1419         // See above comments for NSOtherMouseDown
   1420         ev_button = [e buttonNumber] + 1;
   1421         ev_button = (ev_button == 3) ? 2 : (ev_button + 4);
   1422         ev_type = ButtonRelease;
   1423         goto handle_mouse;
   1424 
   1425     case NSRightMouseUp:
   1426         ev_button = 3;
   1427         ev_type = ButtonRelease;
   1428         goto handle_mouse;
   1429 
   1430     case NSLeftMouseDragged:
   1431         ev_button = 1;
   1432         ev_type = MotionNotify;
   1433         goto handle_mouse;
   1434 
   1435     case NSOtherMouseDragged:
   1436         // See above comments for NSOtherMouseDown
   1437         ev_button = [e buttonNumber] + 1;
   1438         ev_button = (ev_button == 3) ? 2 : (ev_button + 4);
   1439         ev_type = MotionNotify;
   1440         goto handle_mouse;
   1441 
   1442     case NSRightMouseDragged:
   1443         ev_button = 3;
   1444         ev_type = MotionNotify;
   1445         goto handle_mouse;
   1446 
   1447     case NSMouseMoved:
   1448         ev_button = 0;
   1449         ev_type = MotionNotify;
   1450         goto handle_mouse;
   1451 
   1452     case NSTabletPoint:
   1453         ev_button = 0;
   1454         ev_type = MotionNotify;
   1455         goto handle_mouse;
   1456 
   1457 handle_mouse:
   1458         pDev = darwinPointer;
   1459 
   1460         /* NSTabletPoint can have no subtype */
   1461         if ([e type] != NSTabletPoint &&
   1462             [e subtype] == NSTabletProximityEventSubtype) {
   1463             switch ([e pointingDeviceType]) {
   1464             case NSEraserPointingDevice:
   1465                 darwinTabletCurrent = darwinTabletEraser;
   1466                 break;
   1467 
   1468             case NSPenPointingDevice:
   1469                 darwinTabletCurrent = darwinTabletStylus;
   1470                 break;
   1471 
   1472             case NSCursorPointingDevice:
   1473             case NSUnknownPointingDevice:
   1474             default:
   1475                 darwinTabletCurrent = darwinTabletCursor;
   1476                 break;
   1477             }
   1478 
   1479             if ([e isEnteringProximity])
   1480                 needsProximityIn = YES;
   1481             else
   1482                 DarwinSendTabletEvents(darwinTabletCurrent, ProximityOut, 0,
   1483                                        location.x, location.y, pressure,
   1484                                        tilt.x, tilt.y);
   1485             return;
   1486         }
   1487 
   1488         if ([e type] == NSTabletPoint ||
   1489             [e subtype] == NSTabletPointEventSubtype) {
   1490             pressure = [e pressure];
   1491             tilt = [e tilt];
   1492 
   1493             pDev = darwinTabletCurrent;
   1494 
   1495             if (needsProximityIn) {
   1496                 DarwinSendTabletEvents(darwinTabletCurrent, ProximityIn, 0,
   1497                                        location.x, location.y, pressure,
   1498                                        tilt.x, tilt.y);
   1499 
   1500                 needsProximityIn = NO;
   1501             }
   1502         }
   1503 
   1504         if (!XQuartzServerVisible && noTestExtensions) {
   1505             xp_window_id wid = 0;
   1506             xp_error err;
   1507 
   1508             /* Sigh. Need to check that we're really over one of
   1509              * our windows. (We need to receive pointer events while
   1510              * not in the foreground, but we don't want to receive them
   1511              * when another window is over us or we might show a tooltip)
   1512              */
   1513 
   1514             err = xp_find_window(location.x, location.y, 0, &wid);
   1515 
   1516             if (err != XP_Success || (err == XP_Success && wid == 0))
   1517             {
   1518                 bgMouseLocation = location;
   1519                 bgMouseLocationUpdated = TRUE;
   1520                 return;
   1521             }
   1522         }
   1523 
   1524         if (bgMouseLocationUpdated) {
   1525             if (!(ev_type == MotionNotify && ev_button == 0)) {
   1526                 DarwinSendPointerEvents(darwinPointer, MotionNotify, 0,
   1527                                         location.x, location.y,
   1528                                         0.0, 0.0);
   1529             }
   1530             bgMouseLocationUpdated = FALSE;
   1531         }
   1532 
   1533         if (pDev == darwinPointer) {
   1534             DarwinSendPointerEvents(pDev, ev_type, ev_button,
   1535                                     location.x, location.y,
   1536                                     [e deltaX], [e deltaY]);
   1537         } else {
   1538             DarwinSendTabletEvents(pDev, ev_type, ev_button,
   1539                                    location.x, location.y, pressure,
   1540                                    tilt.x, tilt.y);
   1541         }
   1542 
   1543         break;
   1544 
   1545     case NSTabletProximity:
   1546         switch ([e pointingDeviceType]) {
   1547         case NSEraserPointingDevice:
   1548             darwinTabletCurrent = darwinTabletEraser;
   1549             break;
   1550 
   1551         case NSPenPointingDevice:
   1552             darwinTabletCurrent = darwinTabletStylus;
   1553             break;
   1554 
   1555         case NSCursorPointingDevice:
   1556         case NSUnknownPointingDevice:
   1557         default:
   1558             darwinTabletCurrent = darwinTabletCursor;
   1559             break;
   1560         }
   1561 
   1562         if ([e isEnteringProximity])
   1563             needsProximityIn = YES;
   1564         else
   1565             DarwinSendTabletEvents(darwinTabletCurrent, ProximityOut, 0,
   1566                                    location.x, location.y, pressure,
   1567                                    tilt.x, tilt.y);
   1568         break;
   1569 
   1570     case NSScrollWheel:
   1571     {
   1572         CGFloat deltaX = [e deltaX];
   1573         CGFloat deltaY = [e deltaY];
   1574         CGEventRef cge = [e CGEvent];
   1575         BOOL isContinuous =
   1576             CGEventGetIntegerValueField(cge, kCGScrollWheelEventIsContinuous);
   1577 
   1578 #if 0
   1579         /* Scale the scroll value by line height */
   1580         CGEventSourceRef source = CGEventCreateSourceFromEvent(cge);
   1581         if (source) {
   1582             double lineHeight = CGEventSourceGetPixelsPerLine(source);
   1583             CFRelease(source);
   1584             
   1585             /* There's no real reason for the 1/5 ratio here other than that
   1586              * it feels like a good ratio after some testing.
   1587              */
   1588             
   1589             deltaX *= lineHeight / 5.0;
   1590             deltaY *= lineHeight / 5.0;
   1591         }
   1592 #endif
   1593         
   1594         if (XQuartzScrollInDeviceDirection &&
   1595             [e isDirectionInvertedFromDevice]) {
   1596             deltaX *= -1;
   1597             deltaY *= -1;
   1598         }
   1599         /* This hack is in place to better deal with "clicky" scroll wheels:
   1600          * http://xquartz.macosforge.org/trac/ticket/562
   1601          */
   1602         if (!isContinuous) {
   1603             static NSTimeInterval lastScrollTime = 0.0;
   1604 
   1605             /* These store how much extra we have already scrolled.
   1606              * ie, this is how much we ignore on the next event.
   1607              */
   1608             static double deficit_x = 0.0;
   1609             static double deficit_y = 0.0;
   1610 
   1611             /* If we have past a second since the last scroll, wipe the slate
   1612              * clean
   1613              */
   1614             if ([e timestamp] - lastScrollTime > 1.0) {
   1615                 deficit_x = deficit_y = 0.0;
   1616             }
   1617             lastScrollTime = [e timestamp];
   1618 
   1619             if (deltaX != 0.0) {
   1620                 /* If we changed directions, wipe the slate clean */
   1621                 if ((deficit_x < 0.0 && deltaX > 0.0) ||
   1622                     (deficit_x > 0.0 && deltaX < 0.0)) {
   1623                     deficit_x = 0.0;
   1624                 }
   1625 
   1626                 /* Eat up the deficit, but ensure that something is
   1627                  * always sent 
   1628                  */
   1629                 if (fabs(deltaX) > fabs(deficit_x)) {
   1630                     deltaX -= deficit_x;
   1631 
   1632                     if (deltaX > 0.0) {
   1633                         deficit_x = ceil(deltaX) - deltaX;
   1634                         deltaX = ceil(deltaX);
   1635                     } else {
   1636                         deficit_x = floor(deltaX) - deltaX;
   1637                         deltaX = floor(deltaX);
   1638                     }
   1639                 } else {
   1640                     deficit_x -= deltaX;
   1641 
   1642                     if (deltaX > 0.0) {
   1643                         deltaX = 1.0;
   1644                     } else {
   1645                         deltaX = -1.0;
   1646                     }
   1647 
   1648                     deficit_x += deltaX;
   1649                 }
   1650             }
   1651 
   1652             if (deltaY != 0.0) {
   1653                 /* If we changed directions, wipe the slate clean */
   1654                 if ((deficit_y < 0.0 && deltaY > 0.0) ||
   1655                     (deficit_y > 0.0 && deltaY < 0.0)) {
   1656                     deficit_y = 0.0;
   1657                 }
   1658 
   1659                 /* Eat up the deficit, but ensure that something is
   1660                  * always sent 
   1661                  */
   1662                 if (fabs(deltaY) > fabs(deficit_y)) {
   1663                     deltaY -= deficit_y;
   1664 
   1665                     if (deltaY > 0.0) {
   1666                         deficit_y = ceil(deltaY) - deltaY;
   1667                         deltaY = ceil(deltaY);
   1668                     } else {
   1669                         deficit_y = floor(deltaY) - deltaY;
   1670                         deltaY = floor(deltaY);
   1671                     }
   1672                 } else {
   1673                     deficit_y -= deltaY;
   1674 
   1675                     if (deltaY > 0.0) {
   1676                         deltaY = 1.0;
   1677                     } else {
   1678                         deltaY = -1.0;
   1679                     }
   1680 
   1681                     deficit_y += deltaY;
   1682                 }
   1683             }
   1684         }
   1685 
   1686         DarwinSendScrollEvents(deltaX, deltaY);
   1687         break;
   1688     }
   1689 
   1690     case NSKeyDown:
   1691     case NSKeyUp:
   1692     {
   1693         /* XKB clobbers our keymap at startup, so we need to force it on the first keypress.
   1694          * TODO: Make this less of a kludge.
   1695          */
   1696         static int force_resync_keymap = YES;
   1697         if (force_resync_keymap) {
   1698             DarwinSendDDXEvent(kXquartzReloadKeymap, 0);
   1699             force_resync_keymap = NO;
   1700         }
   1701     }
   1702 
   1703         if (darwinSyncKeymap) {
   1704             TISInputSourceRef key_layout = 
   1705                 TISCopyCurrentKeyboardLayoutInputSource();
   1706             TISInputSourceRef clear;
   1707             if (CFEqual(key_layout, last_key_layout)) {
   1708                 CFRelease(key_layout);
   1709             }
   1710             else {
   1711                 /* Swap/free thread-safely */
   1712                 clear = last_key_layout;
   1713                 last_key_layout = key_layout;
   1714                 CFRelease(clear);
   1715 
   1716                 /* Update keyInfo */
   1717                 if (!QuartsResyncKeymap(TRUE)) {
   1718                     ErrorF(
   1719                         "sendX11NSEvent: Could not build a valid keymap.\n");
   1720                 }
   1721             }
   1722         }
   1723 
   1724         ev_type = ([e type] == NSKeyDown) ? KeyPress : KeyRelease;
   1725         DarwinSendKeyboardEvents(ev_type, [e keyCode]);
   1726         break;
   1727 
   1728     default:
   1729         break;              /* for gcc */
   1730     }
   1731 }
   1732 @end