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