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