1321 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			Objective-C
		
	
	
	
			
		
		
	
	
			1321 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			Objective-C
		
	
	
	
| /* X11Application.m -- subclass of NSApplication to multiplex events
 | |
|  
 | |
|  Copyright (c) 2002-2008 Apple Inc.
 | |
|  
 | |
|  Permission is hereby granted, free of charge, to any person
 | |
|  obtaining a copy of this software and associated documentation files
 | |
|  (the "Software"), to deal in the Software without restriction,
 | |
|  including without limitation the rights to use, copy, modify, merge,
 | |
|  publish, distribute, sublicense, and/or sell copies of the Software,
 | |
|  and to permit persons to whom the Software is furnished to do so,
 | |
|  subject to the following conditions:
 | |
|  
 | |
|  The above copyright notice and this permission notice shall be
 | |
|  included in all copies or substantial portions of the Software.
 | |
|  
 | |
|  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | |
|  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | |
|  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 | |
|  NONINFRINGEMENT.  IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
 | |
|  HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 | |
|  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
|  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | |
|  DEALINGS IN THE SOFTWARE.
 | |
|  
 | |
|  Except as contained in this notice, the name(s) of the above
 | |
|  copyright holders shall not be used in advertising or otherwise to
 | |
|  promote the sale, use or other dealings in this Software without
 | |
|  prior written authorization. */
 | |
| 
 | |
| #include "sanitizedCarbon.h"
 | |
| 
 | |
| #ifdef HAVE_DIX_CONFIG_H
 | |
| #include <dix-config.h>
 | |
| #endif
 | |
| 
 | |
| #include "quartzCommon.h"
 | |
| 
 | |
| #import "X11Application.h"
 | |
| 
 | |
| #include "darwin.h"
 | |
| #include "quartz.h"
 | |
| #include "darwinEvents.h"
 | |
| #include "quartzKeyboard.h"
 | |
| #include "quartz.h"
 | |
| #include <X11/extensions/applewmconst.h>
 | |
| #include "micmap.h"
 | |
| #include "exglobals.h"
 | |
| 
 | |
| #include <mach/mach.h>
 | |
| #include <unistd.h>
 | |
| #include <AvailabilityMacros.h>
 | |
| 
 | |
| #include <Xplugin.h>
 | |
| 
 | |
| // pbproxy/pbproxy.h
 | |
| extern int xpbproxy_run (void);
 | |
| 
 | |
| #define DEFAULTS_FILE X11LIBDIR"/X11/xserver/Xquartz.plist"
 | |
| 
 | |
| #ifndef XSERVER_VERSION
 | |
| #define XSERVER_VERSION "?"
 | |
| #endif
 | |
| 
 | |
| /* Stuck modifier / button state... force release when we context switch */
 | |
| static NSEventType keyState[NUM_KEYCODES];
 | |
| 
 | |
| extern Bool noTestExtensions;
 | |
| 
 | |
| #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
 | |
| static TISInputSourceRef last_key_layout;
 | |
| #else
 | |
| static KeyboardLayoutRef last_key_layout;
 | |
| #endif
 | |
| 
 | |
| extern int darwinFakeButtons;
 | |
| 
 | |
| /* Store the mouse location while in the background, and update X11's pointer
 | |
|  * location when we become the foreground application
 | |
|  */
 | |
| static NSPoint bgMouseLocation;
 | |
| static BOOL bgMouseLocationUpdated = FALSE;
 | |
| 
 | |
| X11Application *X11App;
 | |
| 
 | |
| CFStringRef app_prefs_domain_cfstr = NULL;
 | |
| 
 | |
| #define ALL_KEY_MASKS (NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask)
 | |
| 
 | |
| @interface X11Application (Private)
 | |
| - (void) sendX11NSEvent:(NSEvent *)e;
 | |
| @end
 | |
| 
 | |
| @implementation X11Application
 | |
| 
 | |
| typedef struct message_struct message;
 | |
| struct message_struct {
 | |
|     mach_msg_header_t hdr;
 | |
|     SEL selector;
 | |
|     NSObject *arg;
 | |
| };
 | |
| 
 | |
| static mach_port_t _port;
 | |
| 
 | |
| /* Quartz mode initialization routine. This is often dynamically loaded
 | |
|    but is statically linked into this X server. */
 | |
| Bool QuartzModeBundleInit(void);
 | |
| 
 | |
| static void init_ports (void) {
 | |
|     kern_return_t r;
 | |
|     NSPort *p;
 | |
| 	
 | |
|     if (_port != MACH_PORT_NULL) return;
 | |
| 	
 | |
|     r = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &_port);
 | |
|     if (r != KERN_SUCCESS) return;
 | |
| 	
 | |
|     p = [NSMachPort portWithMachPort:_port];
 | |
|     [p setDelegate:NSApp];
 | |
|     [p scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
 | |
| }
 | |
| 
 | |
| static void message_kit_thread (SEL selector, NSObject *arg) {
 | |
|     message msg;
 | |
|     kern_return_t r;
 | |
| 	
 | |
|     msg.hdr.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_MAKE_SEND, 0);
 | |
|     msg.hdr.msgh_size = sizeof (msg);
 | |
|     msg.hdr.msgh_remote_port = _port;
 | |
|     msg.hdr.msgh_local_port = MACH_PORT_NULL;
 | |
|     msg.hdr.msgh_reserved = 0;
 | |
|     msg.hdr.msgh_id = 0;
 | |
| 	
 | |
|     msg.selector = selector;
 | |
|     msg.arg = [arg retain];
 | |
| 	
 | |
|     r = mach_msg (&msg.hdr, MACH_SEND_MSG, msg.hdr.msgh_size,
 | |
| 		  0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
 | |
|     if (r != KERN_SUCCESS)
 | |
| 		ErrorF("%s: mach_msg failed: %x\n", __FUNCTION__, r);
 | |
| }
 | |
| 
 | |
| - (void) handleMachMessage:(void *)_msg {
 | |
|     message *msg = _msg;
 | |
| 	
 | |
|     [self performSelector:msg->selector withObject:msg->arg];
 | |
|     [msg->arg release];
 | |
| }
 | |
| 
 | |
| - (void) set_controller:obj {
 | |
|     if (_controller == nil) _controller = [obj retain];
 | |
| }
 | |
| 
 | |
| - (void) dealloc {
 | |
|     if (_controller != nil) [_controller release];
 | |
| 	
 | |
|     if (_port != MACH_PORT_NULL)
 | |
| 		mach_port_deallocate (mach_task_self (), _port);
 | |
| 	
 | |
|     [super dealloc];
 | |
| }
 | |
| 
 | |
| - (void) orderFrontStandardAboutPanel: (id) sender {
 | |
|     NSMutableDictionary *dict;
 | |
|     NSDictionary *infoDict;
 | |
|     NSString *tem;
 | |
|     
 | |
|     dict = [NSMutableDictionary dictionaryWithCapacity:3];
 | |
|     infoDict = [[NSBundle mainBundle] infoDictionary];
 | |
|     
 | |
|     [dict setObject: NSLocalizedString (@"The X Window System", @"About panel")
 | |
|           forKey:@"ApplicationName"];
 | |
|     
 | |
|     tem = [infoDict objectForKey:@"CFBundleShortVersionString"];
 | |
|     
 | |
|     [dict setObject:[NSString stringWithFormat:@"XQuartz %@", tem]
 | |
|           forKey:@"ApplicationVersion"];
 | |
| 
 | |
|     [dict setObject:[NSString stringWithFormat:@"xorg-server %s", XSERVER_VERSION]
 | |
|           forKey:@"Version"];
 | |
|     
 | |
|     [self orderFrontStandardAboutPanelWithOptions: dict];
 | |
| }
 | |
| 
 | |
| - (void) activateX:(OSX_BOOL)state {
 | |
|     size_t i;
 | |
|     DEBUG_LOG("state=%d, _x_active=%d, \n", state, _x_active)
 | |
|     if (state) {
 | |
|         if(bgMouseLocationUpdated) {
 | |
|             DarwinSendPointerEvents(darwinPointer, MotionNotify, 0, bgMouseLocation.x, bgMouseLocation.y, 0.0, 0.0, 0.0);
 | |
|             bgMouseLocationUpdated = FALSE;
 | |
|         }
 | |
|         DarwinSendDDXEvent(kXquartzActivate, 0);
 | |
|     } else {
 | |
| 
 | |
|         if(darwin_all_modifier_flags)
 | |
|             DarwinUpdateModKeys(0);
 | |
|         for(i=0; i < NUM_KEYCODES; i++) {
 | |
|             if(keyState[i] == NSKeyDown) {
 | |
|                 DarwinSendKeyboardEvents(KeyRelease, i);
 | |
|                 keyState[i] = NSKeyUp;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         DarwinSendDDXEvent(kXquartzDeactivate, 0);
 | |
|     }
 | |
| 
 | |
|     _x_active = state;
 | |
| }
 | |
| 
 | |
| - (void) became_key:(NSWindow *)win {
 | |
| 	[self activateX:NO];
 | |
| }
 | |
| 
 | |
| - (void) sendEvent:(NSEvent *)e {
 | |
|     OSX_BOOL for_appkit, for_x;
 | |
|     
 | |
|     /* By default pass down the responder chain and to X. */
 | |
|     for_appkit = YES;
 | |
|     for_x = YES;
 | |
|     
 | |
|     switch ([e type]) {
 | |
|         case NSLeftMouseDown: case NSRightMouseDown: case NSOtherMouseDown:
 | |
|         case NSLeftMouseUp: case NSRightMouseUp: case NSOtherMouseUp:
 | |
|             if ([e window] != nil) {
 | |
|                 /* Pointer event has an (AppKit) window. Probably something for the kit. */
 | |
|                 for_x = NO;
 | |
|                 if (_x_active) [self activateX:NO];
 | |
|             } else if ([self modalWindow] == nil) {
 | |
|                 /* Must be an X window. Tell appkit it doesn't have focus. */
 | |
|                 for_appkit = NO;
 | |
|                 
 | |
|                 if ([self isActive]) {
 | |
|                     [self deactivate];
 | |
|                     if (!_x_active && quartzProcs->IsX11Window([e window],
 | |
|                                                                [e windowNumber]))
 | |
|                         [self activateX:YES];
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             /* We want to force sending to appkit if we're over the menu bar */
 | |
|             if(!for_appkit) {
 | |
|                 NSPoint NSlocation = [e locationInWindow];
 | |
|                 NSWindow *window = [e window];
 | |
|                 NSRect NSframe, NSvisibleFrame;
 | |
|                 CGRect CGframe, CGvisibleFrame;
 | |
|                 CGPoint CGlocation;
 | |
| 
 | |
|                 if (window != nil)	{
 | |
|                     NSRect frame = [window frame];
 | |
|                     NSlocation.x += frame.origin.x;
 | |
|                     NSlocation.y += frame.origin.y;
 | |
|                 }
 | |
| 
 | |
|                 NSframe = [[NSScreen mainScreen] frame];
 | |
|                 NSvisibleFrame = [[NSScreen mainScreen] visibleFrame];
 | |
|                 
 | |
|                 CGframe = CGRectMake(NSframe.origin.x, NSframe.origin.y,
 | |
|                                             NSframe.size.width, NSframe.size.height);
 | |
|                 CGvisibleFrame = CGRectMake(NSvisibleFrame.origin.x,
 | |
|                                                    NSvisibleFrame.origin.y,
 | |
|                                                    NSvisibleFrame.size.width,
 | |
|                                                    NSvisibleFrame.size.height);
 | |
|                 CGlocation = CGPointMake(NSlocation.x, NSlocation.y);
 | |
|                 
 | |
|                 if(CGRectContainsPoint(CGframe, CGlocation) &&
 | |
|                    !CGRectContainsPoint(CGvisibleFrame, CGlocation))
 | |
|                     for_appkit = YES;
 | |
|             }
 | |
|             
 | |
|             break;
 | |
|             
 | |
|         case NSKeyDown: case NSKeyUp:
 | |
|             
 | |
|             if(_x_active) {
 | |
|                 static BOOL do_swallow = NO;
 | |
|                 static int swallow_keycode;
 | |
|                 
 | |
|                 if([e type] == NSKeyDown) {
 | |
|                     /* Before that though, see if there are any global
 | |
|                      * shortcuts bound to it. */
 | |
| 
 | |
|                     if(darwinAppKitModMask & [e modifierFlags]) {
 | |
|                         /* Override to force sending to Appkit */
 | |
|                         swallow_keycode = [e keyCode];
 | |
|                         do_swallow = YES;
 | |
|                         for_x = NO;
 | |
| #if XPLUGIN_VERSION >= 1
 | |
|                     } else if(XQuartzEnableKeyEquivalents &&
 | |
|                              xp_is_symbolic_hotkey_event([e eventRef])) {
 | |
|                         swallow_keycode = [e keyCode];
 | |
|                         do_swallow = YES;
 | |
|                         for_x = NO;
 | |
| #endif
 | |
|                     } else if(XQuartzEnableKeyEquivalents &&
 | |
|                               [[self mainMenu] performKeyEquivalent:e]) {
 | |
|                         swallow_keycode = [e keyCode];
 | |
|                         do_swallow = YES;
 | |
|                         for_appkit = NO;
 | |
|                         for_x = NO;
 | |
|                     } else if(!XQuartzIsRootless
 | |
|                               && ([e modifierFlags] & ALL_KEY_MASKS) == (NSCommandKeyMask | NSAlternateKeyMask)
 | |
|                               && ([e keyCode] == 0 /*a*/ || [e keyCode] == 53 /*Esc*/)) {
 | |
|                         /* We have this here to force processing fullscreen 
 | |
|                          * toggle even if XQuartzEnableKeyEquivalents is disabled */
 | |
|                         swallow_keycode = [e keyCode];
 | |
|                         do_swallow = YES;
 | |
|                         for_x = NO;
 | |
|                         for_appkit = NO;
 | |
|                         DarwinSendDDXEvent(kXquartzToggleFullscreen, 0);
 | |
|                     } else {
 | |
|                         /* No kit window is focused, so send it to X. */
 | |
|                         for_appkit = NO;
 | |
|                     }
 | |
|                 } else { /* KeyUp */
 | |
|                     /* If we saw a key equivalent on the down, don't pass
 | |
|                      * the up through to X. */
 | |
|                     if (do_swallow && [e keyCode] == swallow_keycode) {
 | |
|                         do_swallow = NO;
 | |
|                         for_x = NO;
 | |
|                     }
 | |
|                 }
 | |
|             } else { /* !_x_active */
 | |
|                 for_x = NO;
 | |
|             }
 | |
|             break;
 | |
|             
 | |
|         case NSFlagsChanged:
 | |
|             /* Don't tell X11 about modifiers changing while it's not active */
 | |
|             if (!_x_active)
 | |
|                 for_x = NO;
 | |
|             break;
 | |
|             
 | |
|         case NSAppKitDefined:
 | |
|             switch ([e subtype]) {
 | |
|                 case NSApplicationActivatedEventType:
 | |
|                     for_x = NO;
 | |
|                     if ([self modalWindow] == nil) {
 | |
|                         BOOL order_all_windows = YES, workspaces, ok;
 | |
|                         for_appkit = NO;
 | |
|                         
 | |
|                         /* FIXME: hack to avoid having to pass the event to appkit,
 | |
|                          which would cause it to raise one of its windows. */
 | |
|                         _appFlags._active = YES;
 | |
|                         
 | |
|                         [self activateX:YES];
 | |
|                         
 | |
|                         /* Get the Spaces preference for SwitchOnActivate */
 | |
|                         (void)CFPreferencesAppSynchronize(CFSTR("com.apple.dock"));
 | |
|                         workspaces = CFPreferencesGetAppBooleanValue(CFSTR("workspaces"), CFSTR("com.apple.dock"), &ok);
 | |
|                         if (!ok)
 | |
|                             workspaces = NO;
 | |
| 
 | |
|                         if (workspaces) {
 | |
|                             (void)CFPreferencesAppSynchronize(CFSTR(".GlobalPreferences"));
 | |
|                             order_all_windows = CFPreferencesGetAppBooleanValue(CFSTR("AppleSpacesSwitchOnActivate"), CFSTR(".GlobalPreferences"), &ok);
 | |
|                             if (!ok)
 | |
|                                 order_all_windows = YES;
 | |
|                         }
 | |
|                         
 | |
|                         /* TODO: In the workspaces && !AppleSpacesSwitchOnActivate case, the windows are ordered
 | |
|                          *       correctly, but we need to activate the top window on this space if there is
 | |
|                          *       none active.
 | |
|                          *
 | |
|                          *       If there are no active windows, and there are minimized windows, we should
 | |
|                          *       be restoring one of them.
 | |
|                          */
 | |
|                         if ([e data2] & 0x10) // 0x10 is set when we use cmd-tab or the dock icon
 | |
|                             DarwinSendDDXEvent(kXquartzBringAllToFront, 1, order_all_windows);
 | |
|                     }
 | |
|                     break;
 | |
|                     
 | |
|                 case 18: /* ApplicationDidReactivate */
 | |
|                     if (XQuartzFullscreenVisible) for_appkit = NO;
 | |
|                     break;
 | |
|                     
 | |
|                 case NSApplicationDeactivatedEventType:
 | |
|                     for_x = NO;
 | |
|                     [self activateX:NO];
 | |
|                     break;
 | |
|             }
 | |
|             break;
 | |
|             
 | |
|         default: break; /* for gcc */
 | |
|     }
 | |
|     
 | |
|     if (for_appkit) [super sendEvent:e];
 | |
|     
 | |
|     if (for_x) [self sendX11NSEvent:e];
 | |
| }
 | |
| 
 | |
| - (void) set_window_menu:(NSArray *)list {
 | |
| 	[_controller set_window_menu:list];
 | |
| }
 | |
| 
 | |
| - (void) set_window_menu_check:(NSNumber *)n {
 | |
| 	[_controller set_window_menu_check:n];
 | |
| }
 | |
| 
 | |
| - (void) set_apps_menu:(NSArray *)list {
 | |
| 	[_controller set_apps_menu:list];
 | |
| }
 | |
| 
 | |
| - (void) set_front_process:unused {
 | |
| 	[NSApp activateIgnoringOtherApps:YES];
 | |
| 
 | |
| 	if ([self modalWindow] == nil)
 | |
| 		[self activateX:YES];
 | |
| }
 | |
| 
 | |
| - (void) set_can_quit:(NSNumber *)state {
 | |
| 	[_controller set_can_quit:[state boolValue]];
 | |
| }
 | |
| 
 | |
| - (void) server_ready:unused {
 | |
| 	[_controller server_ready];
 | |
| }
 | |
| 
 | |
| - (void) show_hide_menubar:(NSNumber *)state {
 | |
|     /* Also shows/hides the dock */
 | |
|     if ([state boolValue])
 | |
|         SetSystemUIMode(kUIModeNormal, 0); 
 | |
|     else
 | |
|         SetSystemUIMode(kUIModeAllHidden, XQuartzFullscreenMenu ? kUIOptionAutoShowMenuBar : 0); // kUIModeAllSuppressed or kUIOptionAutoShowMenuBar can be used to allow "mouse-activation"
 | |
| }
 | |
| 
 | |
| - (void) launch_client:(NSString *)cmd {
 | |
|     (void)[_controller application:self openFile:cmd];
 | |
| }
 | |
| 
 | |
| /* user preferences */
 | |
| 
 | |
| /* Note that these functions only work for arrays whose elements
 | |
|  can be toll-free-bridged between NS and CF worlds. */
 | |
| 
 | |
| static const void *cfretain (CFAllocatorRef a, const void *b) {
 | |
|     return CFRetain (b);
 | |
| }
 | |
| 
 | |
| static void cfrelease (CFAllocatorRef a, const void *b) {
 | |
|     CFRelease (b);
 | |
| }
 | |
| 
 | |
| static CFMutableArrayRef nsarray_to_cfarray (NSArray *in) {
 | |
| 	CFMutableArrayRef out;
 | |
| 	CFArrayCallBacks cb;
 | |
| 	NSObject *ns;
 | |
| 	const CFTypeRef *cf;
 | |
| 	int i, count;
 | |
| 
 | |
| 	memset (&cb, 0, sizeof (cb));
 | |
| 	cb.version = 0;
 | |
| 	cb.retain = cfretain;
 | |
| 	cb.release = cfrelease;
 | |
| 
 | |
| 	count = [in count];
 | |
| 	out = CFArrayCreateMutable (NULL, count, &cb);
 | |
| 
 | |
| 	for (i = 0; i < count; i++) {
 | |
| 		ns = [in objectAtIndex:i];
 | |
| 
 | |
| 		if ([ns isKindOfClass:[NSArray class]])
 | |
| 			cf = (CFTypeRef) nsarray_to_cfarray ((NSArray *) ns);
 | |
| 		else
 | |
| 			cf = CFRetain ((CFTypeRef) ns);
 | |
| 
 | |
| 		CFArrayAppendValue (out, cf);
 | |
| 		CFRelease (cf);
 | |
| 	}
 | |
| 
 | |
| 	return out;
 | |
| }
 | |
| 
 | |
| static NSMutableArray * cfarray_to_nsarray (CFArrayRef in) {
 | |
| 	NSMutableArray *out;
 | |
| 	const CFTypeRef *cf;
 | |
| 	NSObject *ns;
 | |
| 	int i, count;
 | |
| 
 | |
| 	count = CFArrayGetCount (in);
 | |
| 	out = [[NSMutableArray alloc] initWithCapacity:count];
 | |
| 
 | |
| 	for (i = 0; i < count; i++) {
 | |
| 		cf = CFArrayGetValueAtIndex (in, i);
 | |
| 
 | |
| 		if (CFGetTypeID (cf) == CFArrayGetTypeID ())
 | |
| 			ns = cfarray_to_nsarray ((CFArrayRef) cf);
 | |
| 		else
 | |
| 			ns = [(id)cf retain];
 | |
| 
 | |
| 		[out addObject:ns];
 | |
| 		[ns release];
 | |
| 	}
 | |
| 
 | |
| 	return out;
 | |
| }
 | |
| 
 | |
| - (CFPropertyListRef) prefs_get_copy:(NSString *)key {
 | |
|     CFPropertyListRef value;
 | |
| 	
 | |
|     value = CFPreferencesCopyAppValue ((CFStringRef) key, app_prefs_domain_cfstr);
 | |
| 	
 | |
|     if (value == NULL) {
 | |
|       static CFDictionaryRef defaults;
 | |
|       
 | |
|       if (defaults == NULL) {
 | |
| 	CFStringRef error = NULL;
 | |
| 	CFDataRef data;
 | |
| 	CFURLRef url;
 | |
| 	SInt32 error_code;
 | |
| 	
 | |
| 	url = (CFURLCreateFromFileSystemRepresentation
 | |
| 	       (NULL, (unsigned char *)DEFAULTS_FILE, strlen (DEFAULTS_FILE), false));
 | |
| 	if (CFURLCreateDataAndPropertiesFromResource (NULL, url, &data,
 | |
| 						      NULL, NULL, &error_code)) {
 | |
| 	  defaults = (CFPropertyListCreateFromXMLData
 | |
| 		      (NULL, data, kCFPropertyListMutableContainersAndLeaves, &error));
 | |
| 	  if (error != NULL) CFRelease (error);
 | |
| 	  CFRelease (data);
 | |
| 	}
 | |
| 	CFRelease (url);
 | |
| 			
 | |
| 	if (defaults != NULL) {
 | |
| 	  NSMutableArray *apps, *elt;
 | |
| 	  int count, i;
 | |
| 	  NSString *name, *nname;
 | |
| 	  
 | |
| 	  /* Localize the names in the default apps menu. */
 | |
| 	  
 | |
| 	  apps = [(NSDictionary *)defaults objectForKey:@PREFS_APPSMENU];
 | |
| 	  if (apps != nil) {
 | |
| 	    count = [apps count];
 | |
| 	    for (i = 0; i < count; i++)	{
 | |
| 	      elt = [apps objectAtIndex:i];
 | |
| 	      if (elt != nil && [elt isKindOfClass:[NSArray class]]) {
 | |
| 		name = [elt objectAtIndex:0];
 | |
| 		if (name != nil) {
 | |
| 		  nname = NSLocalizedString (name, nil);
 | |
| 		  if (nname != nil && nname != name)
 | |
| 		    [elt replaceObjectAtIndex:0 withObject:nname];
 | |
| 		}
 | |
| 	      }
 | |
| 	    }
 | |
| 	  }
 | |
| 	}
 | |
|       }
 | |
| 		
 | |
|       if (defaults != NULL) value = CFDictionaryGetValue (defaults, key);
 | |
|       if (value != NULL) CFRetain (value);
 | |
|     }
 | |
| 	
 | |
|     return value;
 | |
| }
 | |
| 
 | |
| - (int) prefs_get_integer:(NSString *)key default:(int)def {
 | |
|   CFPropertyListRef value;
 | |
|   int ret;
 | |
|   
 | |
|   value = [self prefs_get_copy:key];
 | |
|   
 | |
|   if (value != NULL && CFGetTypeID (value) == CFNumberGetTypeID ())
 | |
|     CFNumberGetValue (value, kCFNumberIntType, &ret);
 | |
|   else if (value != NULL && CFGetTypeID (value) == CFStringGetTypeID ())
 | |
|     ret = CFStringGetIntValue (value);
 | |
|   else
 | |
|     ret = def;
 | |
|   
 | |
|   if (value != NULL) CFRelease (value);
 | |
|   
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| - (const char *) prefs_get_string:(NSString *)key default:(const char *)def {
 | |
|   CFPropertyListRef value;
 | |
|   const char *ret = NULL;
 | |
|   
 | |
|   value = [self prefs_get_copy:key];
 | |
|   
 | |
|   if (value != NULL && CFGetTypeID (value) == CFStringGetTypeID ()) {
 | |
|     NSString *s = (NSString *) value;
 | |
|     
 | |
|     ret = [s UTF8String];
 | |
|   }
 | |
|   
 | |
|   if (value != NULL) CFRelease (value);
 | |
|   
 | |
|   return ret != NULL ? ret : def;
 | |
| }
 | |
| 
 | |
| - (NSURL *) prefs_copy_url:(NSString *)key default:(NSURL *)def {
 | |
|     CFPropertyListRef value;
 | |
|     NSURL *ret = NULL;
 | |
|     
 | |
|     value = [self prefs_get_copy:key];
 | |
|     
 | |
|     if (value != NULL && CFGetTypeID (value) == CFStringGetTypeID ()) {
 | |
|         NSString *s = (NSString *) value;
 | |
| 
 | |
|         ret = [NSURL URLWithString:s];
 | |
|         [ret retain];
 | |
|     }
 | |
|     
 | |
|     if (value != NULL) CFRelease (value);
 | |
|     
 | |
|     return ret != NULL ? ret : def;
 | |
| }
 | |
| 
 | |
| - (float) prefs_get_float:(NSString *)key default:(float)def {
 | |
|   CFPropertyListRef value;
 | |
|   float ret = def;
 | |
|   
 | |
|   value = [self prefs_get_copy:key];
 | |
|   
 | |
|   if (value != NULL
 | |
|       && CFGetTypeID (value) == CFNumberGetTypeID ()
 | |
|       && CFNumberIsFloatType (value))
 | |
|     CFNumberGetValue (value, kCFNumberFloatType, &ret);
 | |
|   else if (value != NULL && CFGetTypeID (value) == CFStringGetTypeID ())
 | |
|     ret = CFStringGetDoubleValue (value);
 | |
| 	
 | |
|   if (value != NULL) CFRelease (value);
 | |
|   
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| - (int) prefs_get_boolean:(NSString *)key default:(int)def {
 | |
|   CFPropertyListRef value;
 | |
|   int ret = def;
 | |
|   
 | |
|   value = [self prefs_get_copy:key];
 | |
|   
 | |
|   if (value != NULL) {
 | |
|     if (CFGetTypeID (value) == CFNumberGetTypeID ())
 | |
|       CFNumberGetValue (value, kCFNumberIntType, &ret);
 | |
|     else if (CFGetTypeID (value) == CFBooleanGetTypeID ())
 | |
|       ret = CFBooleanGetValue (value);
 | |
|     else if (CFGetTypeID (value) == CFStringGetTypeID ()) {
 | |
|       const char *tem = [(NSString *) value UTF8String];
 | |
|       if (strcasecmp (tem, "true") == 0 || strcasecmp (tem, "yes") == 0)
 | |
| 	ret = YES;
 | |
|       else
 | |
| 	ret = NO;
 | |
|     }
 | |
|     
 | |
|     CFRelease (value);
 | |
|   }
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| - (NSArray *) prefs_get_array:(NSString *)key {
 | |
|   NSArray *ret = nil;
 | |
|   CFPropertyListRef value;
 | |
|   
 | |
|   value = [self prefs_get_copy:key];
 | |
|   
 | |
|   if (value != NULL) {
 | |
|     if (CFGetTypeID (value) == CFArrayGetTypeID ())
 | |
|       ret = [cfarray_to_nsarray (value) autorelease];
 | |
|     
 | |
|     CFRelease (value);
 | |
|   }
 | |
|   
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| - (void) prefs_set_integer:(NSString *)key value:(int)value {
 | |
|     CFNumberRef x;
 | |
| 	
 | |
|     x = CFNumberCreate (NULL, kCFNumberIntType, &value);
 | |
| 	
 | |
|     CFPreferencesSetValue ((CFStringRef) key, (CFTypeRef) x, app_prefs_domain_cfstr,
 | |
| 			   kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
 | |
| 	
 | |
|     CFRelease (x);
 | |
| }
 | |
| 
 | |
| - (void) prefs_set_float:(NSString *)key value:(float)value {
 | |
|     CFNumberRef x;
 | |
| 	
 | |
|     x = CFNumberCreate (NULL, kCFNumberFloatType, &value);
 | |
| 	
 | |
|     CFPreferencesSetValue ((CFStringRef) key, (CFTypeRef) x, app_prefs_domain_cfstr,
 | |
| 			   kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
 | |
| 	
 | |
|     CFRelease (x);
 | |
| }
 | |
| 
 | |
| - (void) prefs_set_boolean:(NSString *)key value:(int)value {
 | |
|   CFPreferencesSetValue ((CFStringRef) key,
 | |
| 			 (CFTypeRef) (value ? kCFBooleanTrue
 | |
| 			 : kCFBooleanFalse), app_prefs_domain_cfstr,
 | |
| 			 kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
 | |
|   
 | |
| }
 | |
| 
 | |
| - (void) prefs_set_array:(NSString *)key value:(NSArray *)value {
 | |
|   CFArrayRef cfarray;
 | |
|   
 | |
|   cfarray = nsarray_to_cfarray (value);
 | |
|   CFPreferencesSetValue ((CFStringRef) key,
 | |
| 			 (CFTypeRef) cfarray,
 | |
| 			 app_prefs_domain_cfstr,
 | |
| 			 kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
 | |
|   CFRelease (cfarray);
 | |
| }
 | |
| 
 | |
| - (void) prefs_set_string:(NSString *)key value:(NSString *)value {
 | |
|   CFPreferencesSetValue ((CFStringRef) key, (CFTypeRef) value,
 | |
| 			 app_prefs_domain_cfstr, kCFPreferencesCurrentUser,
 | |
| 			 kCFPreferencesAnyHost);
 | |
| }
 | |
| 
 | |
| - (void) prefs_synchronize {
 | |
|     CFPreferencesAppSynchronize (kCFPreferencesCurrentApplication);
 | |
| }
 | |
| 
 | |
| - (void) read_defaults
 | |
| {
 | |
|     NSString *nsstr;
 | |
|     const char *tem;
 | |
| 	
 | |
|     XQuartzRootlessDefault = [self prefs_get_boolean:@PREFS_ROOTLESS
 | |
|                                            default:XQuartzRootlessDefault];
 | |
|     XQuartzFullscreenMenu = [self prefs_get_boolean:@PREFS_FULLSCREEN_MENU
 | |
|                                            default:XQuartzFullscreenMenu];
 | |
|     XQuartzFullscreenDisableHotkeys = ![self prefs_get_boolean:@PREFS_FULLSCREEN_HOTKEYS
 | |
|                                                       default:!XQuartzFullscreenDisableHotkeys];
 | |
|     darwinFakeButtons = [self prefs_get_boolean:@PREFS_FAKEBUTTONS
 | |
|                                         default:darwinFakeButtons];
 | |
|     XQuartzOptionSendsAlt = [self prefs_get_boolean:@PREFS_OPTION_SENDS_ALT
 | |
|                                            default:XQuartzOptionSendsAlt];
 | |
| 
 | |
|     if (darwinFakeButtons) {
 | |
|         const char *fake2, *fake3;
 | |
| 
 | |
|         fake2 = [self prefs_get_string:@PREFS_FAKE_BUTTON2 default:NULL];
 | |
|         fake3 = [self prefs_get_string:@PREFS_FAKE_BUTTON3 default:NULL];
 | |
| 
 | |
|         if (fake2 != NULL) darwinFakeMouse2Mask = DarwinParseModifierList(fake2, TRUE);
 | |
|         if (fake3 != NULL) darwinFakeMouse3Mask = DarwinParseModifierList(fake3, TRUE);
 | |
|     }
 | |
| 
 | |
|     tem = [self prefs_get_string:@PREFS_APPKIT_MODIFIERS default:NULL];
 | |
|     if (tem != NULL) darwinAppKitModMask = DarwinParseModifierList(tem, TRUE);
 | |
| 	
 | |
|     tem = [self prefs_get_string:@PREFS_WINDOW_ITEM_MODIFIERS default:NULL];
 | |
|     if (tem != NULL) {
 | |
|         windowItemModMask = DarwinParseModifierList(tem, FALSE);
 | |
|     } else {
 | |
|         nsstr = NSLocalizedString (@"window item modifiers", @"window item modifiers");
 | |
|         if(nsstr != NULL) {
 | |
|             tem = [nsstr UTF8String];
 | |
|             if((tem != NULL) && strcmp(tem, "window item modifiers")) {
 | |
|                 windowItemModMask = DarwinParseModifierList(tem, FALSE);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     XQuartzEnableKeyEquivalents = [self prefs_get_boolean:@PREFS_KEYEQUIVS
 | |
|                                               default:XQuartzEnableKeyEquivalents];
 | |
| 	
 | |
|     darwinSyncKeymap = [self prefs_get_boolean:@PREFS_SYNC_KEYMAP
 | |
|                                        default:darwinSyncKeymap];
 | |
| 		
 | |
|     darwinDesiredDepth = [self prefs_get_integer:@PREFS_DEPTH
 | |
|                                          default:darwinDesiredDepth];
 | |
|     
 | |
|     noTestExtensions = ![self prefs_get_boolean:@PREFS_TEST_EXTENSIONS
 | |
|                                         default:FALSE];
 | |
| 
 | |
| #if XQUARTZ_SPARKLE
 | |
|     NSURL *url =  [self prefs_copy_url:@PREFS_UPDATE_FEED default:nil];
 | |
|     if(url) {
 | |
|         [[SUUpdater sharedUpdater] setFeedURL:url];
 | |
|         [url release];
 | |
|     }
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /* This will end up at the end of the responder chain. */
 | |
| - (void) copy:sender {
 | |
|   DarwinSendDDXEvent(kXquartzPasteboardNotify, 1,
 | |
| 			     AppleWMCopyToPasteboard);
 | |
| }
 | |
| 
 | |
| - (X11Controller *) controller {
 | |
|     return _controller;
 | |
| }
 | |
| 
 | |
| - (OSX_BOOL) x_active {
 | |
|     return _x_active;
 | |
| }
 | |
| 
 | |
| @end
 | |
| 
 | |
| static NSArray *
 | |
| array_with_strings_and_numbers (int nitems, const char **items,
 | |
| 				const char *numbers) {
 | |
|   NSMutableArray *array, *subarray;
 | |
|   NSString *string, *number;
 | |
|   int i;
 | |
| 	
 | |
|   /* (Can't autorelease on the X server thread) */
 | |
|   
 | |
|   array = [[NSMutableArray alloc] initWithCapacity:nitems];
 | |
|   
 | |
|   for (i = 0; i < nitems; i++) {
 | |
|     subarray = [[NSMutableArray alloc] initWithCapacity:2];
 | |
|     
 | |
|     string = [[NSString alloc] initWithUTF8String:items[i]];
 | |
|     [subarray addObject:string];
 | |
|     [string release];
 | |
|     
 | |
|     if (numbers[i] != 0) {
 | |
|       number = [[NSString alloc] initWithFormat:@"%d", numbers[i]];
 | |
|       [subarray addObject:number];
 | |
|       [number release];
 | |
|     } else
 | |
|       [subarray addObject:@""];
 | |
|     
 | |
|     [array addObject:subarray];
 | |
|     [subarray release];
 | |
|   }
 | |
|   
 | |
|   return array;
 | |
| }
 | |
| 
 | |
| void X11ApplicationSetWindowMenu (int nitems, const char **items,
 | |
| 				  const char *shortcuts) {
 | |
|   NSArray *array;
 | |
|   array = array_with_strings_and_numbers (nitems, items, shortcuts);
 | |
|   
 | |
|   /* Send the array of strings over to the appkit thread */
 | |
|   
 | |
|   message_kit_thread (@selector (set_window_menu:), array);
 | |
|   [array release];
 | |
| }
 | |
| 
 | |
| void X11ApplicationSetWindowMenuCheck (int idx) {
 | |
|   NSNumber *n;
 | |
|   
 | |
|   n = [[NSNumber alloc] initWithInt:idx];
 | |
|   
 | |
|   message_kit_thread (@selector (set_window_menu_check:), n);
 | |
|   
 | |
|   [n release];
 | |
| }
 | |
| 
 | |
| void X11ApplicationSetFrontProcess (void) {
 | |
|     message_kit_thread (@selector (set_front_process:), nil);
 | |
| }
 | |
| 
 | |
| void X11ApplicationSetCanQuit (int state) {
 | |
|     NSNumber *n;
 | |
| 	
 | |
|     n = [[NSNumber alloc] initWithBool:state];
 | |
| 	
 | |
|     message_kit_thread (@selector (set_can_quit:), n);
 | |
| 	
 | |
|     [n release];
 | |
| }
 | |
| 
 | |
| void X11ApplicationServerReady (void) {
 | |
|     message_kit_thread (@selector (server_ready:), nil);
 | |
| }
 | |
| 
 | |
| void X11ApplicationShowHideMenubar (int state) {
 | |
|     NSNumber *n;
 | |
| 	
 | |
|     n = [[NSNumber alloc] initWithBool:state];
 | |
| 	
 | |
|     message_kit_thread (@selector (show_hide_menubar:), n);
 | |
| 	
 | |
|     [n release];
 | |
| }
 | |
| 
 | |
| void X11ApplicationLaunchClient (const char *cmd) {
 | |
|     NSString *string;
 | |
|     
 | |
|     string = [[NSString alloc] initWithUTF8String:cmd];
 | |
| 	
 | |
|     message_kit_thread (@selector (launch_client:), string);
 | |
| 	
 | |
|     [string release];
 | |
| }
 | |
| 
 | |
| static void check_xinitrc (void) {
 | |
|     char *tem, buf[1024];
 | |
|     NSString *msg;
 | |
| 	
 | |
|     if ([X11App prefs_get_boolean:@PREFS_DONE_XINIT_CHECK default:NO])
 | |
| 		return;
 | |
| 	
 | |
|     tem = getenv ("HOME");
 | |
|     if (tem == NULL) goto done;
 | |
| 	
 | |
|     snprintf (buf, sizeof (buf), "%s/.xinitrc", tem);
 | |
|     if (access (buf, F_OK) != 0)
 | |
| 		goto done;
 | |
| 	
 | |
|     msg = NSLocalizedString (@"You have an existing ~/.xinitrc file.\n\n\
 | |
| Windows displayed by X11 applications may not have titlebars, or may look \
 | |
| different to windows displayed by native applications.\n\n\
 | |
| Would you like to move aside the existing file and use the standard X11 \
 | |
| environment the next time you start X11?", @"Startup xinitrc dialog");
 | |
| 
 | |
|     if(NSAlertDefaultReturn == NSRunAlertPanel (nil, msg, NSLocalizedString (@"Yes", @""),
 | |
|                                                 NSLocalizedString (@"No", @""), nil)) {
 | |
|         char buf2[1024];
 | |
|         int i = -1;
 | |
|       
 | |
|         snprintf (buf2, sizeof (buf2), "%s.old", buf);
 | |
|       
 | |
|         for(i = 1; access (buf2, F_OK) == 0; i++)
 | |
|             snprintf (buf2, sizeof (buf2), "%s.old.%d", buf, i);
 | |
| 
 | |
|         rename (buf, buf2);
 | |
|     }
 | |
|     
 | |
|  done:
 | |
|     [X11App prefs_set_boolean:@PREFS_DONE_XINIT_CHECK value:YES];
 | |
|     [X11App prefs_synchronize];
 | |
| }
 | |
| 
 | |
| static inline pthread_t create_thread(void *func, void *arg) {
 | |
|     pthread_attr_t attr;
 | |
|     pthread_t tid;
 | |
|     
 | |
|     pthread_attr_init(&attr);
 | |
|     pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
 | |
|     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 | |
|     pthread_create(&tid, &attr, func, arg);
 | |
|     pthread_attr_destroy(&attr);
 | |
|     
 | |
|     return tid;
 | |
| }
 | |
| 
 | |
| static void *xpbproxy_x_thread(void *args) {
 | |
|     xpbproxy_run();
 | |
| 
 | |
|     fprintf(stderr, "xpbproxy thread is terminating unexpectedly.\n");
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| void X11ApplicationMain (int argc, char **argv, char **envp) {
 | |
|     NSAutoreleasePool *pool;
 | |
| 
 | |
| #ifdef DEBUG
 | |
|     while (access ("/tmp/x11-block", F_OK) == 0) sleep (1);
 | |
| #endif
 | |
|   
 | |
|     pool = [[NSAutoreleasePool alloc] init];
 | |
|     X11App = (X11Application *) [X11Application sharedApplication];
 | |
|     init_ports ();
 | |
|     
 | |
|     app_prefs_domain_cfstr = (CFStringRef)[[NSBundle mainBundle] bundleIdentifier];
 | |
| 
 | |
|     [NSApp read_defaults];
 | |
|     [NSBundle loadNibNamed:@"main" owner:NSApp];
 | |
|     [[NSNotificationCenter defaultCenter] addObserver:NSApp
 | |
| 					selector:@selector (became_key:)
 | |
| 					name:NSWindowDidBecomeKeyNotification object:nil];
 | |
| 
 | |
|     /*
 | |
|      * The xpr Quartz mode is statically linked into this server.
 | |
|      * Initialize all the Quartz functions.
 | |
|      */
 | |
|     QuartzModeBundleInit();
 | |
| 
 | |
|     /* Calculate the height of the menubar so we can avoid it. */
 | |
|     aquaMenuBarHeight = NSHeight([[NSScreen mainScreen] frame]) -
 | |
|     NSMaxY([[NSScreen mainScreen] visibleFrame]);
 | |
| 
 | |
|     /* Set the key layout seed before we start the server */
 | |
| #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
 | |
|     last_key_layout = TISCopyCurrentKeyboardLayoutInputSource();    
 | |
| 
 | |
|     if(!last_key_layout)
 | |
|         fprintf(stderr, "X11ApplicationMain: Unable to determine TISCopyCurrentKeyboardLayoutInputSource() at startup.\n");
 | |
| #else
 | |
|     KLGetCurrentKeyboardLayout(&last_key_layout);
 | |
|     if(!last_key_layout)
 | |
|         fprintf(stderr, "X11ApplicationMain: Unable to determine KLGetCurrentKeyboardLayout() at startup.\n");
 | |
| #endif
 | |
| 
 | |
|     if (!QuartsResyncKeymap(FALSE)) {
 | |
|         fprintf(stderr, "X11ApplicationMain: Could not build a valid keymap.\n");
 | |
|     }
 | |
| 
 | |
|     /* Tell the server thread that it can proceed */
 | |
|     QuartzInitServer(argc, argv, envp);
 | |
|     
 | |
|     /* This must be done after QuartzInitServer because it can result in
 | |
|      * an mieqEnqueue() - <rdar://problem/6300249>
 | |
|      */
 | |
|     check_xinitrc();
 | |
|     
 | |
|     create_thread(xpbproxy_x_thread, NULL);
 | |
| 
 | |
| #if XQUARTZ_SPARKLE
 | |
|     [[X11App controller] setup_sparkle];
 | |
|     [[SUUpdater sharedUpdater] resetUpdateCycle];
 | |
| //    [[SUUpdater sharedUpdater] checkForUpdates:X11App];
 | |
| #endif
 | |
| 
 | |
|     [pool release];
 | |
|     [NSApp run];
 | |
|     /* not reached */
 | |
| }
 | |
| 
 | |
| @implementation X11Application (Private)
 | |
| 
 | |
| #ifdef NX_DEVICELCMDKEYMASK
 | |
| /* This is to workaround a bug in the VNC server where we sometimes see the L
 | |
|  * modifier and sometimes see no "side"
 | |
|  */
 | |
| static inline int ensure_flag(int flags, int device_independent, int device_dependents, int device_dependent_default) {
 | |
|     if( (flags & device_independent) &&
 | |
|        !(flags & device_dependents))
 | |
|         flags |= device_dependent_default;
 | |
|     return flags;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef DEBUG_UNTRUSTED_POINTER_DELTA
 | |
| static const char *untrusted_str(NSEvent *e) {
 | |
|     switch([e type]) {
 | |
|         case NSScrollWheel:
 | |
|             return "NSScrollWheel";
 | |
|         case NSTabletPoint:
 | |
|             return "NSTabletPoint";
 | |
|         case NSOtherMouseDown:
 | |
|             return "NSOtherMouseDown";
 | |
|         case NSOtherMouseUp:
 | |
|             return "NSOtherMouseUp";
 | |
|         case NSLeftMouseDown:
 | |
|             return "NSLeftMouseDown";
 | |
|         case NSLeftMouseUp:
 | |
|             return "NSLeftMouseUp";
 | |
|         default:
 | |
|             switch([e subtype]) {
 | |
|                 case NSTabletPointEventSubtype:
 | |
|                     return "NSTabletPointEventSubtype";
 | |
|                 case NSTabletProximityEventSubtype:
 | |
|                     return "NSTabletProximityEventSubtype";
 | |
|                 default:
 | |
|                     return "Other";
 | |
|             }
 | |
|     }
 | |
| }
 | |
| #endif
 | |
| 
 | |
| - (void) sendX11NSEvent:(NSEvent *)e {
 | |
|     NSPoint location = NSZeroPoint, tilt = NSZeroPoint;
 | |
|     int ev_button, ev_type;
 | |
|     float pressure = 0.0;
 | |
|     DeviceIntPtr pDev;
 | |
|     int modifierFlags;
 | |
|     BOOL isMouseOrTabletEvent, isTabletEvent;
 | |
| 
 | |
|     isMouseOrTabletEvent =  [e type] == NSLeftMouseDown    ||  [e type] == NSOtherMouseDown    ||  [e type] == NSRightMouseDown    ||
 | |
|                             [e type] == NSLeftMouseUp      ||  [e type] == NSOtherMouseUp      ||  [e type] == NSRightMouseUp      ||
 | |
|                             [e type] == NSLeftMouseDragged ||  [e type] == NSOtherMouseDragged ||  [e type] == NSRightMouseDragged ||
 | |
|                             [e type] == NSMouseMoved       ||  [e type] == NSTabletPoint       ||  [e type] == NSScrollWheel;
 | |
| 
 | |
|     isTabletEvent = ([e type] == NSTabletPoint) ||
 | |
|                     (isMouseOrTabletEvent && ([e subtype] == NSTabletPointEventSubtype || [e subtype] == NSTabletProximityEventSubtype));
 | |
| 
 | |
|     if(isMouseOrTabletEvent) {
 | |
|         static NSPoint lastpt;
 | |
|         NSWindow *window = [e window];
 | |
|         NSRect screen = [[[NSScreen screens] objectAtIndex:0] frame];
 | |
| 	    BOOL hasUntrustedPointerDelta;
 | |
|         
 | |
|         // NSEvents for tablets are not consistent wrt deltaXY between events, so we cannot rely on that
 | |
|         // Thus tablets will be subject to the warp-pointer bug worked around by the delta, but tablets
 | |
|         // are not normally used in cases where that bug would present itself, so this is a fair tradeoff
 | |
|         // <rdar://problem/7111003> deltaX and deltaY are incorrect for NSMouseMoved, NSTabletPointEventSubtype
 | |
|         // http://xquartz.macosforge.org/trac/ticket/288
 | |
|         hasUntrustedPointerDelta = isTabletEvent;
 | |
|         
 | |
|         // The deltaXY for middle click events also appear erroneous after fast user switching
 | |
|         // <rdar://problem/7979468> deltaX and deltaY are incorrect for NSOtherMouseDown and NSOtherMouseUp after FUS
 | |
|         // http://xquartz.macosforge.org/trac/ticket/389
 | |
|         hasUntrustedPointerDelta = hasUntrustedPointerDelta || [e type] == NSOtherMouseDown || [e type] == NSOtherMouseUp;
 | |
| 
 | |
|         // The deltaXY for scroll events correspond to the scroll delta, not the pointer delta
 | |
|         // <rdar://problem/7989690> deltaXY for wheel events are being sent as mouse movement
 | |
|         hasUntrustedPointerDelta = hasUntrustedPointerDelta || [e type] == NSScrollWheel;
 | |
| 
 | |
| #ifdef DEBUG_UNTRUSTED_POINTER_DELTA
 | |
|         hasUntrustedPointerDelta = hasUntrustedPointerDelta || [e type] == NSLeftMouseDown || [e type] == NSLeftMouseUp;
 | |
| #endif
 | |
|         
 | |
|         if (window != nil)	{
 | |
|             NSRect frame = [window frame];
 | |
|             location = [e locationInWindow];
 | |
|             location.x += frame.origin.x;
 | |
|             location.y += frame.origin.y;
 | |
|             lastpt = location;
 | |
|         } else if(hasUntrustedPointerDelta) {
 | |
| #ifdef DEBUG_UNTRUSTED_POINTER_DELTA
 | |
|             ErrorF("--- Begin Event Debug ---\n");
 | |
|             ErrorF("Event type: %s\n", untrusted_str(e));
 | |
|             ErrorF("old lastpt: (%0.2f, %0.2f)\n", lastpt.x, lastpt.y);
 | |
|             ErrorF("     delta: (%0.2f, %0.2f)\n", [e deltaX], -[e deltaY]);
 | |
|             ErrorF("  location: (%0.2f, %0.2f)\n", lastpt.x + [e deltaX], lastpt.y - [e deltaY]);
 | |
|             ErrorF("workaround: (%0.2f, %0.2f)\n", [e locationInWindow].x, [e locationInWindow].y);
 | |
|             ErrorF("--- End Event Debug ---\n");
 | |
| 
 | |
|             location.x = lastpt.x + [e deltaX];
 | |
|             location.y = lastpt.y - [e deltaY];
 | |
|             lastpt = [e locationInWindow];
 | |
| #else
 | |
|             location = [e locationInWindow];
 | |
|             lastpt = location;
 | |
| #endif
 | |
|         } else {
 | |
|             location.x = lastpt.x + [e deltaX];
 | |
|             location.y = lastpt.y - [e deltaY];
 | |
|             lastpt = [e locationInWindow];
 | |
|         }
 | |
|         
 | |
|         /* Convert coordinate system */
 | |
|         location.y = (screen.origin.y + screen.size.height) - location.y;
 | |
|     }
 | |
|     
 | |
|     modifierFlags = [e modifierFlags];
 | |
|     
 | |
| #ifdef NX_DEVICELCMDKEYMASK
 | |
|     /* This is to workaround a bug in the VNC server where we sometimes see the L
 | |
|      * modifier and sometimes see no "side"
 | |
|      */
 | |
|     modifierFlags = ensure_flag(modifierFlags, NX_CONTROLMASK,   NX_DEVICELCTLKEYMASK   | NX_DEVICERCTLKEYMASK,     NX_DEVICELCTLKEYMASK);
 | |
|     modifierFlags = ensure_flag(modifierFlags, NX_SHIFTMASK,     NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK,   NX_DEVICELSHIFTKEYMASK);
 | |
|     modifierFlags = ensure_flag(modifierFlags, NX_COMMANDMASK,   NX_DEVICELCMDKEYMASK   | NX_DEVICERCMDKEYMASK,     NX_DEVICELCMDKEYMASK);
 | |
|     modifierFlags = ensure_flag(modifierFlags, NX_ALTERNATEMASK, NX_DEVICELALTKEYMASK   | NX_DEVICERALTKEYMASK,     NX_DEVICELALTKEYMASK);
 | |
| #endif
 | |
| 
 | |
|     modifierFlags &= darwin_all_modifier_mask;
 | |
| 
 | |
|     /* We don't receive modifier key events while out of focus, and 3button
 | |
|      * emulation mucks this up, so we need to check our modifier flag state
 | |
|      * on every event... ugg
 | |
|      */
 | |
|     
 | |
|     if(darwin_all_modifier_flags != modifierFlags)
 | |
|         DarwinUpdateModKeys(modifierFlags);
 | |
|     
 | |
| 	switch ([e type]) {
 | |
| 		case NSLeftMouseDown:     ev_button=1; ev_type=ButtonPress;   goto handle_mouse;
 | |
| 		case NSOtherMouseDown:    ev_button=2; ev_type=ButtonPress;   goto handle_mouse;
 | |
| 		case NSRightMouseDown:    ev_button=3; ev_type=ButtonPress;   goto handle_mouse;
 | |
| 		case NSLeftMouseUp:       ev_button=1; ev_type=ButtonRelease; goto handle_mouse;
 | |
| 		case NSOtherMouseUp:      ev_button=2; ev_type=ButtonRelease; goto handle_mouse;
 | |
| 		case NSRightMouseUp:      ev_button=3; ev_type=ButtonRelease; goto handle_mouse;
 | |
| 		case NSLeftMouseDragged:  ev_button=1; ev_type=MotionNotify;  goto handle_mouse;
 | |
| 		case NSOtherMouseDragged: ev_button=2; ev_type=MotionNotify;  goto handle_mouse;
 | |
| 		case NSRightMouseDragged: ev_button=3; ev_type=MotionNotify;  goto handle_mouse;
 | |
| 		case NSMouseMoved:        ev_button=0; ev_type=MotionNotify;  goto handle_mouse;
 | |
|         case NSTabletPoint:       ev_button=0; ev_type=MotionNotify;  goto handle_mouse;
 | |
|             
 | |
|         handle_mouse:
 | |
|             pDev = darwinPointer;
 | |
| 
 | |
|             /* NSTabletPoint can have no subtype */
 | |
|             if([e type] != NSTabletPoint &&
 | |
|                [e subtype] == NSTabletProximityEventSubtype) {
 | |
|                 switch([e pointingDeviceType]) {
 | |
|                     case NSEraserPointingDevice:
 | |
|                         darwinTabletCurrent=darwinTabletEraser;
 | |
|                         break;
 | |
|                     case NSPenPointingDevice:
 | |
|                         darwinTabletCurrent=darwinTabletStylus;
 | |
|                         break;
 | |
|                     case NSCursorPointingDevice:
 | |
|                     case NSUnknownPointingDevice:
 | |
|                     default:
 | |
|                         darwinTabletCurrent=darwinTabletCursor;
 | |
|                         break;
 | |
|                 }
 | |
|                 
 | |
|                 /* NSTabletProximityEventSubtype doesn't encode pressure ant tilt
 | |
|                  * So we just pretend the motion was caused by the mouse.  Hopefully
 | |
|                  * we'll have a better solution for this in the future (like maybe
 | |
|                  * NSTabletProximityEventSubtype will come from NSTabletPoint
 | |
|                  * rather than NSMouseMoved.
 | |
|                 pressure = [e pressure];
 | |
|                 tilt     = [e tilt];
 | |
|                 pDev = darwinTabletCurrent;                
 | |
|                  */
 | |
| 
 | |
|                 DarwinSendProximityEvents([e isEnteringProximity] ? ProximityIn : ProximityOut,
 | |
|                                           location.x, location.y);
 | |
|             }
 | |
| 
 | |
| 			if ([e type] == NSTabletPoint || [e subtype] == NSTabletPointEventSubtype) {
 | |
|                 pressure = [e pressure];
 | |
|                 tilt     = [e tilt];
 | |
|                 
 | |
|                 pDev = darwinTabletCurrent;
 | |
|             }
 | |
| 
 | |
|             if(!XQuartzServerVisible && noTestExtensions) {
 | |
| #if defined(XPLUGIN_VERSION) && XPLUGIN_VERSION > 0
 | |
| /* Older libXplugin (Tiger/"Stock" Leopard) aren't thread safe, so we can't call xp_find_window from the Appkit thread */
 | |
|                 xp_window_id wid = 0;
 | |
|                 xp_error e;
 | |
| 
 | |
|                 /* Sigh. Need to check that we're really over one of
 | |
|                  * our windows. (We need to receive pointer events while
 | |
|                  * not in the foreground, but we don't want to receive them
 | |
|                  * when another window is over us or we might show a tooltip)
 | |
|                  */
 | |
| 
 | |
|                 e = xp_find_window(location.x, location.y, 0, &wid);
 | |
| 
 | |
|                 if (e != XP_Success || (e == XP_Success && wid == 0))
 | |
| #endif
 | |
|                 {
 | |
|                     bgMouseLocation = location;
 | |
|                     bgMouseLocationUpdated = TRUE;
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             if(bgMouseLocationUpdated) {
 | |
|                 if(!(ev_type == MotionNotify && ev_button == 0)) {
 | |
|                     DarwinSendPointerEvents(pDev, MotionNotify, 0, location.x,
 | |
|                                             location.y, pressure, tilt.x, tilt.y);
 | |
|                 }
 | |
|                 bgMouseLocationUpdated = FALSE;
 | |
|             }
 | |
| 
 | |
|             DarwinSendPointerEvents(pDev, ev_type, ev_button, location.x, location.y,
 | |
|                                     pressure, tilt.x, tilt.y);
 | |
|             
 | |
|             break;
 | |
|             
 | |
| 		case NSTabletProximity:
 | |
|             switch([e pointingDeviceType]) {
 | |
|                 case NSEraserPointingDevice:
 | |
|                     darwinTabletCurrent=darwinTabletEraser;
 | |
|                     break;
 | |
|                 case NSPenPointingDevice:
 | |
|                     darwinTabletCurrent=darwinTabletStylus;
 | |
|                     break;
 | |
|                 case NSCursorPointingDevice:
 | |
|                 case NSUnknownPointingDevice:
 | |
|                 default:
 | |
|                     darwinTabletCurrent=darwinTabletCursor;
 | |
|                     break;
 | |
|             }
 | |
|             
 | |
| 			DarwinSendProximityEvents([e isEnteringProximity] ? ProximityIn : ProximityOut,
 | |
|                                       location.x, location.y);
 | |
|             break;
 | |
|             
 | |
| 		case NSScrollWheel:
 | |
| #if !defined(XPLUGIN_VERSION) || XPLUGIN_VERSION == 0
 | |
|             /* If we're in the background, we need to send a MotionNotify event
 | |
|              * first, since we aren't getting them on background mouse motion
 | |
|              */
 | |
|             if(!XQuartzServerVisible && noTestExtensions) {
 | |
|                 bgMouseLocationUpdated = FALSE;
 | |
|                 DarwinSendPointerEvents(darwinPointer, MotionNotify, 0, location.x,
 | |
|                                         location.y, pressure, tilt.x, tilt.y);
 | |
|             }
 | |
| #endif
 | |
| 			DarwinSendScrollEvents([e deltaX], [e deltaY], location.x, location.y,
 | |
|                                    pressure, tilt.x, tilt.y);
 | |
|             break;
 | |
|             
 | |
|         case NSKeyDown: case NSKeyUp:
 | |
|             {
 | |
|                 /* XKB clobbers our keymap at startup, so we need to force it on the first keypress.
 | |
|                  * TODO: Make this less of a kludge.
 | |
|                  */
 | |
|                 static int force_resync_keymap = YES;
 | |
|                 if(force_resync_keymap) {
 | |
|                     DarwinSendDDXEvent(kXquartzReloadKeymap, 0);
 | |
|                     force_resync_keymap = NO;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if(darwinSyncKeymap) {
 | |
| #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
 | |
|                 TISInputSourceRef key_layout = TISCopyCurrentKeyboardLayoutInputSource();
 | |
|                 TISInputSourceRef clear;
 | |
|                 if (CFEqual(key_layout, last_key_layout)) {
 | |
|                     CFRelease(key_layout);
 | |
|                 } else {
 | |
|                     /* Swap/free thread-safely */
 | |
|                     clear = last_key_layout;
 | |
|                     last_key_layout = key_layout;
 | |
|                     CFRelease(clear);
 | |
| #else
 | |
|                 KeyboardLayoutRef key_layout;
 | |
|                 KLGetCurrentKeyboardLayout(&key_layout);
 | |
|                 if(key_layout != last_key_layout) {
 | |
|                     last_key_layout = key_layout;
 | |
| #endif
 | |
|                     /* Update keyInfo */
 | |
|                     if (!QuartsResyncKeymap(TRUE)) {
 | |
|                         fprintf(stderr, "sendX11NSEvent: Could not build a valid keymap.\n");
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             /* Avoid stuck keys on context switch */
 | |
|             if(keyState[[e keyCode]] == [e type])
 | |
|                 return;
 | |
|             keyState[[e keyCode]] = [e type];
 | |
| 
 | |
|             DarwinSendKeyboardEvents(([e type] == NSKeyDown) ? KeyPress : KeyRelease, [e keyCode]);
 | |
|             break;
 | |
|             
 | |
|         default: break; /* for gcc */
 | |
| 	}	
 | |
| }
 | |
| @end
 |