jscript: Store concatenated strings as a rope string to avoid useless copying.
[wine] / dlls / winemac.drv / cocoa_window.m
1 /*
2  * MACDRV Cocoa window code
3  *
4  * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #import <Carbon/Carbon.h>
22
23 #import "cocoa_window.h"
24
25 #include "macdrv_cocoa.h"
26 #import "cocoa_app.h"
27 #import "cocoa_event.h"
28 #import "cocoa_opengl.h"
29
30
31 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
32 enum {
33     kVK_RightCommand              = 0x36, /* Invented for Wine; was unused */
34 };
35
36
37 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
38 {
39     NSUInteger style_mask;
40
41     if (wf->title_bar)
42     {
43         style_mask = NSTitledWindowMask;
44         if (wf->close_button) style_mask |= NSClosableWindowMask;
45         if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
46         if (wf->resizable) style_mask |= NSResizableWindowMask;
47         if (wf->utility) style_mask |= NSUtilityWindowMask;
48     }
49     else style_mask = NSBorderlessWindowMask;
50
51     return style_mask;
52 }
53
54
55 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
56 {
57     NSScreen* screen;
58     for (screen in screens)
59     {
60         if (NSIntersectsRect(frame, [screen frame]))
61             return TRUE;
62     }
63     return FALSE;
64 }
65
66
67 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
68 {
69     for (NSScreen* screen in screens)
70     {
71         if (NSContainsRect(rect, [screen frame]))
72             return screen;
73     }
74     return nil;
75 }
76
77
78 /* We rely on the supposedly device-dependent modifier flags to distinguish the
79    keys on the left side of the keyboard from those on the right.  Some event
80    sources don't set those device-depdendent flags.  If we see a device-independent
81    flag for a modifier without either corresponding device-dependent flag, assume
82    the left one. */
83 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
84 {
85     if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
86         *modifiers |= NX_DEVICELCMDKEYMASK;
87     if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
88         *modifiers |= NX_DEVICELSHIFTKEYMASK;
89     if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
90         *modifiers |= NX_DEVICELCTLKEYMASK;
91     if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
92         *modifiers |= NX_DEVICELALTKEYMASK;
93 }
94
95 /* As we manipulate individual bits of a modifier mask, we can end up with
96    inconsistent sets of flags.  In particular, we might set or clear one of the
97    left/right-specific bits, but not the corresponding non-side-specific bit.
98    Fix that.  If either side-specific bit is set, set the non-side-specific bit,
99    otherwise clear it. */
100 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
101 {
102     if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
103         *modifiers |= NX_COMMANDMASK;
104     else
105         *modifiers &= ~NX_COMMANDMASK;
106     if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
107         *modifiers |= NX_SHIFTMASK;
108     else
109         *modifiers &= ~NX_SHIFTMASK;
110     if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
111         *modifiers |= NX_CONTROLMASK;
112     else
113         *modifiers &= ~NX_CONTROLMASK;
114     if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
115         *modifiers |= NX_ALTERNATEMASK;
116     else
117         *modifiers &= ~NX_ALTERNATEMASK;
118 }
119
120
121 @interface WineContentView : NSView <NSTextInputClient>
122 {
123     NSMutableArray* glContexts;
124     NSMutableArray* pendingGlContexts;
125
126     NSMutableAttributedString* markedText;
127     NSRange markedTextSelection;
128 }
129
130     - (void) addGLContext:(WineOpenGLContext*)context;
131     - (void) removeGLContext:(WineOpenGLContext*)context;
132     - (void) updateGLContexts;
133
134 @end
135
136
137 @interface WineWindow ()
138
139 @property (nonatomic) BOOL disabled;
140 @property (nonatomic) BOOL noActivate;
141 @property (readwrite, nonatomic) BOOL floating;
142 @property (retain, nonatomic) NSWindow* latentParentWindow;
143
144 @property (nonatomic) void* hwnd;
145 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
146
147 @property (nonatomic) void* surface;
148 @property (nonatomic) pthread_mutex_t* surface_mutex;
149
150 @property (copy, nonatomic) NSBezierPath* shape;
151 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
152 @property (readonly, nonatomic) BOOL needsTransparency;
153
154 @property (nonatomic) BOOL colorKeyed;
155 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
156 @property (nonatomic) BOOL usePerPixelAlpha;
157
158 @property (readwrite, nonatomic) NSInteger levelWhenActive;
159
160 @property (assign, nonatomic) void* imeData;
161 @property (nonatomic) BOOL commandDone;
162
163 @end
164
165
166 @implementation WineContentView
167
168     - (void) dealloc
169     {
170         [markedText release];
171         [glContexts release];
172         [pendingGlContexts release];
173         [super dealloc];
174     }
175
176     - (BOOL) isFlipped
177     {
178         return YES;
179     }
180
181     - (void) drawRect:(NSRect)rect
182     {
183         WineWindow* window = (WineWindow*)[self window];
184
185         for (WineOpenGLContext* context in pendingGlContexts)
186             context.needsUpdate = TRUE;
187         [glContexts addObjectsFromArray:pendingGlContexts];
188         [pendingGlContexts removeAllObjects];
189
190         if ([window contentView] != self)
191             return;
192
193         if (window.surface && window.surface_mutex &&
194             !pthread_mutex_lock(window.surface_mutex))
195         {
196             const CGRect* rects;
197             int count;
198
199             if (get_surface_blit_rects(window.surface, &rects, &count) && count)
200             {
201                 CGContextRef context;
202                 int i;
203
204                 [window.shape addClip];
205
206                 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
207                 CGContextSetBlendMode(context, kCGBlendModeCopy);
208
209                 for (i = 0; i < count; i++)
210                 {
211                     CGRect imageRect;
212                     CGImageRef image;
213
214                     imageRect = CGRectIntersection(rects[i], NSRectToCGRect(rect));
215                     image = create_surface_image(window.surface, &imageRect, FALSE);
216
217                     if (image)
218                     {
219                         if (window.colorKeyed)
220                         {
221                             CGImageRef maskedImage;
222                             CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
223                                                      window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
224                                                      window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
225                             maskedImage = CGImageCreateWithMaskingColors(image, components);
226                             if (maskedImage)
227                             {
228                                 CGImageRelease(image);
229                                 image = maskedImage;
230                             }
231                         }
232
233                         CGContextDrawImage(context, imageRect, image);
234
235                         CGImageRelease(image);
236                     }
237                 }
238             }
239
240             pthread_mutex_unlock(window.surface_mutex);
241         }
242
243         // If the window may be transparent, then we have to invalidate the
244         // shadow every time we draw.  Also, if this is the first time we've
245         // drawn since changing from transparent to opaque.
246         if (![window isOpaque] || window.shapeChangedSinceLastDraw)
247         {
248             window.shapeChangedSinceLastDraw = FALSE;
249             [window invalidateShadow];
250         }
251     }
252
253     /* By default, NSView will swallow right-clicks in an attempt to support contextual
254        menus.  We need to bypass that and allow the event to make it to the window. */
255     - (void) rightMouseDown:(NSEvent*)theEvent
256     {
257         [[self window] rightMouseDown:theEvent];
258     }
259
260     - (void) addGLContext:(WineOpenGLContext*)context
261     {
262         if (!glContexts)
263             glContexts = [[NSMutableArray alloc] init];
264         if (!pendingGlContexts)
265             pendingGlContexts = [[NSMutableArray alloc] init];
266         [pendingGlContexts addObject:context];
267         [self setNeedsDisplay:YES];
268     }
269
270     - (void) removeGLContext:(WineOpenGLContext*)context
271     {
272         [glContexts removeObjectIdenticalTo:context];
273         [pendingGlContexts removeObjectIdenticalTo:context];
274     }
275
276     - (void) updateGLContexts
277     {
278         for (WineOpenGLContext* context in glContexts)
279             context.needsUpdate = TRUE;
280     }
281
282     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
283     {
284         return YES;
285     }
286
287     - (BOOL) preservesContentDuringLiveResize
288     {
289         // Returning YES from this tells Cocoa to keep our view's content during
290         // a Cocoa-driven resize.  In theory, we're also supposed to override
291         // -setFrameSize: to mark exposed sections as needing redisplay, but
292         // user32 will take care of that in a roundabout way.  This way, we don't
293         // redraw until the window surface is flushed.
294         //
295         // This doesn't do anything when we resize the window ourselves.
296         return YES;
297     }
298
299     - (BOOL)acceptsFirstResponder
300     {
301         return [[self window] contentView] == self;
302     }
303
304     - (void) completeText:(NSString*)text
305     {
306         macdrv_event* event;
307         WineWindow* window = (WineWindow*)[self window];
308
309         event = macdrv_create_event(IM_SET_TEXT, window);
310         event->im_set_text.data = [window imeData];
311         event->im_set_text.text = (CFStringRef)[text copy];
312         event->im_set_text.complete = TRUE;
313
314         [[window queue] postEvent:event];
315
316         macdrv_release_event(event);
317
318         [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
319         markedTextSelection = NSMakeRange(0, 0);
320         [[self inputContext] discardMarkedText];
321     }
322
323     /*
324      * ---------- NSTextInputClient methods ----------
325      */
326     - (NSTextInputContext*) inputContext
327     {
328         if (!markedText)
329             markedText = [[NSMutableAttributedString alloc] init];
330         return [super inputContext];
331     }
332
333     - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
334     {
335         if ([string isKindOfClass:[NSAttributedString class]])
336             string = [string string];
337
338         if ([string isKindOfClass:[NSString class]])
339             [self completeText:string];
340     }
341
342     - (void) doCommandBySelector:(SEL)aSelector
343     {
344         [(WineWindow*)[self window] setCommandDone:TRUE];
345     }
346
347     - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
348     {
349         if ([string isKindOfClass:[NSAttributedString class]])
350             string = [string string];
351
352         if ([string isKindOfClass:[NSString class]])
353         {
354             macdrv_event* event;
355             WineWindow* window = (WineWindow*)[self window];
356
357             if (replacementRange.location == NSNotFound)
358                 replacementRange = NSMakeRange(0, [markedText length]);
359
360             [markedText replaceCharactersInRange:replacementRange withString:string];
361             markedTextSelection = selectedRange;
362             markedTextSelection.location += replacementRange.location;
363
364             event = macdrv_create_event(IM_SET_TEXT, window);
365             event->im_set_text.data = [window imeData];
366             event->im_set_text.text = (CFStringRef)[[markedText string] copy];
367             event->im_set_text.complete = FALSE;
368
369             [[window queue] postEvent:event];
370
371             macdrv_release_event(event);
372
373             event = macdrv_create_event(IM_SET_CURSOR_POS, window);
374             event->im_set_cursor_pos.data = [window imeData];
375             event->im_set_cursor_pos.pos = markedTextSelection.location;
376
377             [[window queue] postEvent:event];
378
379             macdrv_release_event(event);
380         }
381     }
382
383     - (void) unmarkText
384     {
385         [self completeText:nil];
386     }
387
388     - (NSRange) selectedRange
389     {
390         return markedTextSelection;
391     }
392
393     - (NSRange) markedRange
394     {
395         NSRange range = NSMakeRange(0, [markedText length]);
396         if (!range.length)
397             range.location = NSNotFound;
398         return range;
399     }
400
401     - (BOOL) hasMarkedText
402     {
403         return [markedText length] > 0;
404     }
405
406     - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
407     {
408         if (aRange.location >= [markedText length])
409             return nil;
410
411         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
412         if (actualRange)
413             *actualRange = aRange;
414         return [markedText attributedSubstringFromRange:aRange];
415     }
416
417     - (NSArray*) validAttributesForMarkedText
418     {
419         return [NSArray array];
420     }
421
422     - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
423     {
424         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
425         if (actualRange)
426             *actualRange = aRange;
427         return NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
428     }
429
430     - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
431     {
432         return NSNotFound;
433     }
434
435     - (NSInteger) windowLevel
436     {
437         return [[self window] level];
438     }
439
440 @end
441
442
443 @implementation WineWindow
444
445     @synthesize disabled, noActivate, floating, latentParentWindow, hwnd, queue;
446     @synthesize surface, surface_mutex;
447     @synthesize shape, shapeChangedSinceLastDraw;
448     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
449     @synthesize usePerPixelAlpha;
450     @synthesize levelWhenActive;
451     @synthesize imeData, commandDone;
452
453     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
454                                  windowFrame:(NSRect)window_frame
455                                         hwnd:(void*)hwnd
456                                        queue:(WineEventQueue*)queue
457     {
458         WineWindow* window;
459         WineContentView* contentView;
460         NSTrackingArea* trackingArea;
461
462         [[WineApplicationController sharedController] flipRect:&window_frame];
463
464         window = [[[self alloc] initWithContentRect:window_frame
465                                           styleMask:style_mask_for_features(wf)
466                                             backing:NSBackingStoreBuffered
467                                               defer:YES] autorelease];
468
469         if (!window) return nil;
470         window->normalStyleMask = [window styleMask];
471
472         /* Standardize windows to eliminate differences between titled and
473            borderless windows and between NSWindow and NSPanel. */
474         [window setHidesOnDeactivate:NO];
475         [window setReleasedWhenClosed:NO];
476
477         [window disableCursorRects];
478         [window setShowsResizeIndicator:NO];
479         [window setHasShadow:wf->shadow];
480         [window setAcceptsMouseMovedEvents:YES];
481         [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
482         [window setDelegate:window];
483         window.hwnd = hwnd;
484         window.queue = queue;
485
486         [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
487                                                                   (NSString*)kUTTypeContent,
488                                                                   nil]];
489
490         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
491         if (!contentView)
492             return nil;
493         [contentView setAutoresizesSubviews:NO];
494
495         /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
496            because they give us mouse moves in the background. */
497         trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
498                                                      options:(NSTrackingMouseMoved |
499                                                               NSTrackingActiveAlways |
500                                                               NSTrackingInVisibleRect)
501                                                        owner:window
502                                                     userInfo:nil] autorelease];
503         if (!trackingArea)
504             return nil;
505         [contentView addTrackingArea:trackingArea];
506
507         [window setContentView:contentView];
508         [window setInitialFirstResponder:contentView];
509
510         return window;
511     }
512
513     - (void) dealloc
514     {
515         [liveResizeDisplayTimer invalidate];
516         [liveResizeDisplayTimer release];
517         [queue release];
518         [latentParentWindow release];
519         [shape release];
520         [super dealloc];
521     }
522
523     - (void) adjustFeaturesForState
524     {
525         NSUInteger style = normalStyleMask;
526
527         if (self.disabled)
528             style &= ~NSResizableWindowMask;
529         if (style != [self styleMask])
530             [self setStyleMask:style];
531
532         if (style & NSClosableWindowMask)
533             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
534         if (style & NSMiniaturizableWindowMask)
535             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
536     }
537
538     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
539     {
540         normalStyleMask = style_mask_for_features(wf);
541         [self adjustFeaturesForState];
542         [self setHasShadow:wf->shadow];
543     }
544
545     - (void) adjustWindowLevel
546     {
547         WineApplicationController* controller = [WineApplicationController sharedController];
548         NSInteger level;
549         BOOL fullscreen, captured;
550         NSScreen* screen;
551         NSUInteger index;
552         WineWindow* other = nil;
553
554         screen = screen_covered_by_rect([self frame], [NSScreen screens]);
555         fullscreen = (screen != nil);
556         captured = (screen || [self screen]) && [controller areDisplaysCaptured];
557
558         if (captured || fullscreen)
559         {
560             if (captured)
561                 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
562             else
563                 level = NSMainMenuWindowLevel + 1;
564
565             if (self.floating)
566                 level++;
567         }
568         else if (self.floating)
569             level = NSFloatingWindowLevel;
570         else
571             level = NSNormalWindowLevel;
572
573         index = [[controller orderedWineWindows] indexOfObjectIdenticalTo:self];
574         if (index != NSNotFound && index + 1 < [[controller orderedWineWindows] count])
575         {
576             other = [[controller orderedWineWindows] objectAtIndex:index + 1];
577             if (level < [other level])
578                 level = [other level];
579         }
580
581         if (level != [self level])
582         {
583             [self setLevelWhenActive:level];
584
585             /* Setting the window level above has moved this window to the front
586                of all other windows at the same level.  We need to move it
587                back into its proper place among other windows of that level.
588                Also, any windows which are supposed to be in front of it had
589                better have the same or higher window level.  If not, bump them
590                up. */
591             if (index != NSNotFound && [self isVisible])
592             {
593                 for (; index > 0; index--)
594                 {
595                     other = [[controller orderedWineWindows] objectAtIndex:index - 1];
596                     if ([other level] < level)
597                         [other setLevelWhenActive:level];
598                     else
599                     {
600                         [self orderWindow:NSWindowBelow relativeTo:[other windowNumber]];
601                         break;
602                     }
603                 }
604             }
605         }
606     }
607
608     - (void) setMacDrvState:(const struct macdrv_window_state*)state
609     {
610         NSWindowCollectionBehavior behavior;
611
612         self.disabled = state->disabled;
613         self.noActivate = state->no_activate;
614
615         self.floating = state->floating;
616         [self adjustWindowLevel];
617
618         behavior = NSWindowCollectionBehaviorDefault;
619         if (state->excluded_by_expose)
620             behavior |= NSWindowCollectionBehaviorTransient;
621         else
622             behavior |= NSWindowCollectionBehaviorManaged;
623         if (state->excluded_by_cycle)
624         {
625             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
626             if ([self isVisible])
627                 [NSApp removeWindowsItem:self];
628         }
629         else
630         {
631             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
632             if ([self isVisible])
633                 [NSApp addWindowsItem:self title:[self title] filename:NO];
634         }
635         [self setCollectionBehavior:behavior];
636
637         if (state->minimized && ![self isMiniaturized])
638         {
639             ignore_windowMiniaturize = TRUE;
640             [self miniaturize:nil];
641         }
642         else if (!state->minimized && [self isMiniaturized])
643         {
644             ignore_windowDeminiaturize = TRUE;
645             [self deminiaturize:nil];
646         }
647
648         /* Whatever events regarding minimization might have been in the queue are now stale. */
649         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
650                                          event_mask_for_type(WINDOW_DID_UNMINIMIZE)
651                                forWindow:self];
652     }
653
654     /* Returns whether or not the window was ordered in, which depends on if
655        its frame intersects any screen. */
656     - (BOOL) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next
657     {
658         WineApplicationController* controller = [WineApplicationController sharedController];
659         BOOL on_screen = frame_intersects_screens([self frame], [NSScreen screens]);
660         if (on_screen)
661         {
662             [controller transformProcessToForeground];
663
664             if (prev)
665             {
666                 /* Make sure that windows that should be above this one really are.
667                    This is necessary since a full-screen window gets a boost to its
668                    window level to be in front of the menu bar and Dock and that moves
669                    it out of the z-order that Win32 would otherwise establish. */
670                 if ([prev level] < [self level])
671                 {
672                     NSUInteger index = [[controller orderedWineWindows] indexOfObjectIdenticalTo:prev];
673                     if (index != NSNotFound)
674                     {
675                         [prev setLevelWhenActive:[self level]];
676                         for (; index > 0; index--)
677                         {
678                             WineWindow* other = [[controller orderedWineWindows] objectAtIndex:index - 1];
679                             if ([other level] < [self level])
680                                 [other setLevelWhenActive:[self level]];
681                         }
682                     }
683                 }
684                 [self orderWindow:NSWindowBelow relativeTo:[prev windowNumber]];
685                 [controller wineWindow:self ordered:NSWindowBelow relativeTo:prev];
686             }
687             else
688             {
689                 /* Similarly, make sure this window is really above what it should be. */
690                 if (next && [next level] > [self level])
691                     [self setLevelWhenActive:[next level]];
692                 [self orderWindow:NSWindowAbove relativeTo:[next windowNumber]];
693                 [controller wineWindow:self ordered:NSWindowAbove relativeTo:next];
694             }
695             if (latentParentWindow)
696             {
697                 if ([latentParentWindow level] > [self level])
698                     [self setLevelWhenActive:[latentParentWindow level]];
699                 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
700                 [controller wineWindow:self ordered:NSWindowAbove relativeTo:latentParentWindow];
701                 self.latentParentWindow = nil;
702             }
703
704             /* Cocoa may adjust the frame when the window is ordered onto the screen.
705                Generate a frame-changed event just in case.  The back end will ignore
706                it if nothing actually changed. */
707             [self windowDidResize:nil];
708
709             if (![self isExcludedFromWindowsMenu])
710                 [NSApp addWindowsItem:self title:[self title] filename:NO];
711         }
712
713         return on_screen;
714     }
715
716     - (void) doOrderOut
717     {
718         self.latentParentWindow = [self parentWindow];
719         [latentParentWindow removeChildWindow:self];
720         [self orderOut:nil];
721         [[WineApplicationController sharedController] wineWindow:self ordered:NSWindowOut relativeTo:nil];
722         [NSApp removeWindowsItem:self];
723     }
724
725     - (BOOL) setFrameIfOnScreen:(NSRect)contentRect
726     {
727         NSArray* screens = [NSScreen screens];
728         BOOL on_screen = [self isVisible];
729         NSRect frame, oldFrame;
730
731         if (![screens count]) return on_screen;
732
733         /* Origin is (left, top) in a top-down space.  Need to convert it to
734            (left, bottom) in a bottom-up space. */
735         [[WineApplicationController sharedController] flipRect:&contentRect];
736
737         if (on_screen)
738         {
739             on_screen = frame_intersects_screens(contentRect, screens);
740             if (!on_screen)
741                 [self doOrderOut];
742         }
743
744         if (!NSIsEmptyRect(contentRect))
745         {
746             oldFrame = [self frame];
747             frame = [self frameRectForContentRect:contentRect];
748             if (!NSEqualRects(frame, oldFrame))
749             {
750                 if (NSEqualSizes(frame.size, oldFrame.size))
751                     [self setFrameOrigin:frame.origin];
752                 else
753                     [self setFrame:frame display:YES];
754             }
755         }
756
757         if (on_screen)
758         {
759             [self adjustWindowLevel];
760
761             /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
762                event.  The back end will ignore it if nothing actually changed. */
763             [self windowDidResize:nil];
764         }
765         else
766         {
767             /* The back end is establishing a new window size and position.  It's
768                not interested in any stale events regarding those that may be sitting
769                in the queue. */
770             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
771                                    forWindow:self];
772         }
773
774         return on_screen;
775     }
776
777     - (void) setMacDrvParentWindow:(WineWindow*)parent
778     {
779         if ([self parentWindow] != parent)
780         {
781             [[self parentWindow] removeChildWindow:self];
782             self.latentParentWindow = nil;
783             if ([self isVisible] && parent)
784             {
785                 if ([parent level] > [self level])
786                     [self setLevelWhenActive:[parent level]];
787                 [parent addChildWindow:self ordered:NSWindowAbove];
788                 [[WineApplicationController sharedController] wineWindow:self ordered:NSWindowAbove relativeTo:parent];
789             }
790             else
791                 self.latentParentWindow = parent;
792         }
793     }
794
795     - (void) setDisabled:(BOOL)newValue
796     {
797         if (disabled != newValue)
798         {
799             disabled = newValue;
800             [self adjustFeaturesForState];
801         }
802     }
803
804     - (BOOL) needsTransparency
805     {
806         return self.shape || self.colorKeyed || self.usePerPixelAlpha;
807     }
808
809     - (void) checkTransparency
810     {
811         if (![self isOpaque] && !self.needsTransparency)
812         {
813             [self setBackgroundColor:[NSColor windowBackgroundColor]];
814             [self setOpaque:YES];
815         }
816         else if ([self isOpaque] && self.needsTransparency)
817         {
818             [self setBackgroundColor:[NSColor clearColor]];
819             [self setOpaque:NO];
820         }
821     }
822
823     - (void) setShape:(NSBezierPath*)newShape
824     {
825         if (shape == newShape) return;
826         if (shape && newShape && [shape isEqual:newShape]) return;
827
828         if (shape)
829         {
830             [[self contentView] setNeedsDisplayInRect:[shape bounds]];
831             [shape release];
832         }
833         if (newShape)
834             [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
835
836         shape = [newShape copy];
837         self.shapeChangedSinceLastDraw = TRUE;
838
839         [self checkTransparency];
840     }
841
842     - (void) postMouseButtonEvent:(NSEvent *)theEvent pressed:(int)pressed
843     {
844         CGPoint pt = CGEventGetLocation([theEvent CGEvent]);
845         macdrv_event* event;
846
847         event = macdrv_create_event(MOUSE_BUTTON, self);
848         event->mouse_button.button = [theEvent buttonNumber];
849         event->mouse_button.pressed = pressed;
850         event->mouse_button.x = pt.x;
851         event->mouse_button.y = pt.y;
852         event->mouse_button.time_ms = [[WineApplicationController sharedController] ticksForEventTime:[theEvent timestamp]];
853
854         [queue postEvent:event];
855
856         macdrv_release_event(event);
857     }
858
859     - (void) makeFocused
860     {
861         WineApplicationController* controller = [WineApplicationController sharedController];
862         NSArray* screens;
863
864         [controller transformProcessToForeground];
865
866         /* If a borderless window is offscreen, orderFront: won't move
867            it onscreen like it would for a titled window.  Do that ourselves. */
868         screens = [NSScreen screens];
869         if (!([self styleMask] & NSTitledWindowMask) && ![self isVisible] &&
870             !frame_intersects_screens([self frame], screens))
871         {
872             NSScreen* primaryScreen = [screens objectAtIndex:0];
873             NSRect frame = [primaryScreen frame];
874             [self setFrameTopLeftPoint:NSMakePoint(NSMinX(frame), NSMaxY(frame))];
875             frame = [self constrainFrameRect:[self frame] toScreen:primaryScreen];
876             [self setFrame:frame display:YES];
877         }
878
879         if ([[controller orderedWineWindows] count])
880         {
881             WineWindow* front;
882             if (self.floating)
883                 front = [[controller orderedWineWindows] objectAtIndex:0];
884             else
885             {
886                 for (front in [controller orderedWineWindows])
887                     if (!front.floating) break;
888             }
889             if (front && [front levelWhenActive] > [self levelWhenActive])
890                 [self setLevelWhenActive:[front levelWhenActive]];
891         }
892         [self orderFront:nil];
893         [controller wineWindow:self ordered:NSWindowAbove relativeTo:nil];
894         causing_becomeKeyWindow = TRUE;
895         [self makeKeyWindow];
896         causing_becomeKeyWindow = FALSE;
897         if (latentParentWindow)
898         {
899             if ([latentParentWindow level] > [self level])
900                 [self setLevelWhenActive:[latentParentWindow level]];
901             [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
902             [controller wineWindow:self ordered:NSWindowAbove relativeTo:latentParentWindow];
903             self.latentParentWindow = nil;
904         }
905         if (![self isExcludedFromWindowsMenu])
906             [NSApp addWindowsItem:self title:[self title] filename:NO];
907
908         /* Cocoa may adjust the frame when the window is ordered onto the screen.
909            Generate a frame-changed event just in case.  The back end will ignore
910            it if nothing actually changed. */
911         [self windowDidResize:nil];
912     }
913
914     - (void) postKey:(uint16_t)keyCode
915              pressed:(BOOL)pressed
916            modifiers:(NSUInteger)modifiers
917                event:(NSEvent*)theEvent
918     {
919         macdrv_event* event;
920         CGEventRef cgevent;
921         WineApplicationController* controller = [WineApplicationController sharedController];
922
923         event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
924         event->key.keycode   = keyCode;
925         event->key.modifiers = modifiers;
926         event->key.time_ms   = [controller ticksForEventTime:[theEvent timestamp]];
927
928         if ((cgevent = [theEvent CGEvent]))
929         {
930             CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
931                                                         kCGKeyboardEventKeyboardType);
932             if (keyboardType != controller.keyboardType)
933             {
934                 controller.keyboardType = keyboardType;
935                 [controller keyboardSelectionDidChange];
936             }
937         }
938
939         [queue postEvent:event];
940
941         macdrv_release_event(event);
942     }
943
944     - (void) postKeyEvent:(NSEvent *)theEvent
945     {
946         [self flagsChanged:theEvent];
947         [self postKey:[theEvent keyCode]
948               pressed:[theEvent type] == NSKeyDown
949             modifiers:[theEvent modifierFlags]
950                 event:theEvent];
951     }
952
953     - (void) postMouseMovedEvent:(NSEvent *)theEvent absolute:(BOOL)absolute
954     {
955         macdrv_event* event;
956
957         if (absolute)
958         {
959             CGPoint point = CGEventGetLocation([theEvent CGEvent]);
960
961             event = macdrv_create_event(MOUSE_MOVED_ABSOLUTE, self);
962             event->mouse_moved.x = point.x;
963             event->mouse_moved.y = point.y;
964
965             mouseMoveDeltaX = 0;
966             mouseMoveDeltaY = 0;
967         }
968         else
969         {
970             /* Add event delta to accumulated delta error */
971             /* deltaY is already flipped */
972             mouseMoveDeltaX += [theEvent deltaX];
973             mouseMoveDeltaY += [theEvent deltaY];
974
975             event = macdrv_create_event(MOUSE_MOVED, self);
976             event->mouse_moved.x = mouseMoveDeltaX;
977             event->mouse_moved.y = mouseMoveDeltaY;
978
979             /* Keep the remainder after integer truncation. */
980             mouseMoveDeltaX -= event->mouse_moved.x;
981             mouseMoveDeltaY -= event->mouse_moved.y;
982         }
983
984         if (event->type == MOUSE_MOVED_ABSOLUTE || event->mouse_moved.x || event->mouse_moved.y)
985         {
986             event->mouse_moved.time_ms = [[WineApplicationController sharedController] ticksForEventTime:[theEvent timestamp]];
987
988             [queue postEvent:event];
989         }
990
991         macdrv_release_event(event);
992     }
993
994     - (void) setLevelWhenActive:(NSInteger)level
995     {
996         levelWhenActive = level;
997         if (([NSApp isActive] || level <= NSFloatingWindowLevel) &&
998             level != [self level])
999             [self setLevel:level];
1000     }
1001
1002
1003     /*
1004      * ---------- NSWindow method overrides ----------
1005      */
1006     - (BOOL) canBecomeKeyWindow
1007     {
1008         if (causing_becomeKeyWindow) return YES;
1009         if (self.disabled || self.noActivate) return NO;
1010         return [self isKeyWindow];
1011     }
1012
1013     - (BOOL) canBecomeMainWindow
1014     {
1015         return [self canBecomeKeyWindow];
1016     }
1017
1018     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
1019     {
1020         // If a window is sized to completely cover a screen, then it's in
1021         // full-screen mode.  In that case, we don't allow NSWindow to constrain
1022         // it.
1023         NSRect contentRect = [self contentRectForFrameRect:frameRect];
1024         if (!screen_covered_by_rect(contentRect, [NSScreen screens]))
1025             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
1026         return frameRect;
1027     }
1028
1029     - (BOOL) isExcludedFromWindowsMenu
1030     {
1031         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
1032     }
1033
1034     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
1035     {
1036         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
1037             return [self isKeyWindow] || (!self.disabled && !self.noActivate);
1038         return [super validateMenuItem:menuItem];
1039     }
1040
1041     /* We don't call this.  It's the action method of the items in the Window menu. */
1042     - (void) makeKeyAndOrderFront:(id)sender
1043     {
1044         if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1045             [[WineApplicationController sharedController] windowGotFocus:self];
1046     }
1047
1048     - (void) sendEvent:(NSEvent*)event
1049     {
1050         WineApplicationController* controller = [WineApplicationController sharedController];
1051
1052         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
1053            interface control.  For example, Control-Tab switches focus among
1054            views.  We want to bypass that feature, so directly route key-down
1055            events to -keyDown:. */
1056         if ([event type] == NSKeyDown)
1057             [[self firstResponder] keyDown:event];
1058         else
1059         {
1060             if ([event type] == NSLeftMouseDown)
1061             {
1062                 NSWindowButton windowButton;
1063                 BOOL broughtWindowForward = TRUE;
1064
1065                 /* Since our windows generally claim they can't be made key, clicks
1066                    in their title bars are swallowed by the theme frame stuff.  So,
1067                    we hook directly into the event stream and assume that any click
1068                    in the window will activate it, if Wine and the Win32 program
1069                    accept. */
1070                 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1071                     [controller windowGotFocus:self];
1072
1073                 /* Any left-click on our window anyplace other than the close or
1074                    minimize buttons will bring it forward. */
1075                 for (windowButton = NSWindowCloseButton;
1076                      windowButton <= NSWindowMiniaturizeButton;
1077                      windowButton++)
1078                 {
1079                     NSButton* button = [[event window] standardWindowButton:windowButton];
1080                     if (button)
1081                     {
1082                         NSPoint point = [button convertPoint:[event locationInWindow] fromView:nil];
1083                         if ([button mouse:point inRect:[button bounds]])
1084                         {
1085                             broughtWindowForward = FALSE;
1086                             break;
1087                         }
1088                     }
1089                 }
1090
1091                 if (broughtWindowForward)
1092                     [controller wineWindow:self ordered:NSWindowAbove relativeTo:nil];
1093             }
1094
1095             [super sendEvent:event];
1096         }
1097     }
1098
1099
1100     /*
1101      * ---------- NSResponder method overrides ----------
1102      */
1103     - (void) mouseDown:(NSEvent *)theEvent { [self postMouseButtonEvent:theEvent pressed:1]; }
1104     - (void) rightMouseDown:(NSEvent *)theEvent { [self mouseDown:theEvent]; }
1105     - (void) otherMouseDown:(NSEvent *)theEvent { [self mouseDown:theEvent]; }
1106
1107     - (void) mouseUp:(NSEvent *)theEvent { [self postMouseButtonEvent:theEvent pressed:0]; }
1108     - (void) rightMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
1109     - (void) otherMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
1110
1111     - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1112     - (void) keyUp:(NSEvent *)theEvent   { [self postKeyEvent:theEvent]; }
1113
1114     - (void) flagsChanged:(NSEvent *)theEvent
1115     {
1116         static const struct {
1117             NSUInteger  mask;
1118             uint16_t    keycode;
1119         } modifiers[] = {
1120             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
1121             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
1122             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
1123             { NX_DEVICELCTLKEYMASK,     kVK_Control },
1124             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
1125             { NX_DEVICELALTKEYMASK,     kVK_Option },
1126             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
1127             { NX_DEVICELCMDKEYMASK,     kVK_Command },
1128             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
1129         };
1130
1131         NSUInteger modifierFlags = [theEvent modifierFlags];
1132         NSUInteger changed;
1133         int i, last_changed;
1134
1135         fix_device_modifiers_by_generic(&modifierFlags);
1136         changed = modifierFlags ^ lastModifierFlags;
1137
1138         last_changed = -1;
1139         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
1140             if (changed & modifiers[i].mask)
1141                 last_changed = i;
1142
1143         for (i = 0; i <= last_changed; i++)
1144         {
1145             if (changed & modifiers[i].mask)
1146             {
1147                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
1148
1149                 if (i == last_changed)
1150                     lastModifierFlags = modifierFlags;
1151                 else
1152                 {
1153                     lastModifierFlags ^= modifiers[i].mask;
1154                     fix_generic_modifiers_by_device(&lastModifierFlags);
1155                 }
1156
1157                 // Caps lock generates one event for each press-release action.
1158                 // We need to simulate a pair of events for each actual event.
1159                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1160                 {
1161                     [self postKey:modifiers[i].keycode
1162                           pressed:TRUE
1163                         modifiers:lastModifierFlags
1164                             event:(NSEvent*)theEvent];
1165                     pressed = FALSE;
1166                 }
1167
1168                 [self postKey:modifiers[i].keycode
1169                       pressed:pressed
1170                     modifiers:lastModifierFlags
1171                         event:(NSEvent*)theEvent];
1172             }
1173         }
1174     }
1175
1176     - (void) scrollWheel:(NSEvent *)theEvent
1177     {
1178         CGPoint pt;
1179         macdrv_event* event;
1180         CGEventRef cgevent;
1181         CGFloat x, y;
1182         BOOL continuous = FALSE;
1183
1184         cgevent = [theEvent CGEvent];
1185         pt = CGEventGetLocation(cgevent);
1186
1187         event = macdrv_create_event(MOUSE_SCROLL, self);
1188         event->mouse_scroll.x = pt.x;
1189         event->mouse_scroll.y = pt.y;
1190         event->mouse_scroll.time_ms = [[WineApplicationController sharedController] ticksForEventTime:[theEvent timestamp]];
1191
1192         if (CGEventGetIntegerValueField(cgevent, kCGScrollWheelEventIsContinuous))
1193         {
1194             continuous = TRUE;
1195
1196             /* Continuous scroll wheel events come from high-precision scrolling
1197                hardware like Apple's Magic Mouse, Mighty Mouse, and trackpads.
1198                For these, we can get more precise data from the CGEvent API. */
1199             /* Axis 1 is vertical, axis 2 is horizontal. */
1200             x = CGEventGetDoubleValueField(cgevent, kCGScrollWheelEventPointDeltaAxis2);
1201             y = CGEventGetDoubleValueField(cgevent, kCGScrollWheelEventPointDeltaAxis1);
1202         }
1203         else
1204         {
1205             double pixelsPerLine = 10;
1206             CGEventSourceRef source;
1207
1208             /* The non-continuous values are in units of "lines", not pixels. */
1209             if ((source = CGEventCreateSourceFromEvent(cgevent)))
1210             {
1211                 pixelsPerLine = CGEventSourceGetPixelsPerLine(source);
1212                 CFRelease(source);
1213             }
1214
1215             x = pixelsPerLine * [theEvent deltaX];
1216             y = pixelsPerLine * [theEvent deltaY];
1217         }
1218
1219         /* Mac: negative is right or down, positive is left or up.
1220            Win32: negative is left or down, positive is right or up.
1221            So, negate the X scroll value to translate. */
1222         x = -x;
1223
1224         /* The x,y values so far are in pixels.  Win32 expects to receive some
1225            fraction of WHEEL_DELTA == 120.  By my estimation, that's roughly
1226            6 times the pixel value. */
1227         event->mouse_scroll.x_scroll = 6 * x;
1228         event->mouse_scroll.y_scroll = 6 * y;
1229
1230         if (!continuous)
1231         {
1232             /* For non-continuous "clicky" wheels, if there was any motion, make
1233                sure there was at least WHEEL_DELTA motion.  This is so, at slow
1234                speeds where the system's acceleration curve is actually reducing the
1235                scroll distance, the user is sure to get some action out of each click.
1236                For example, this is important for rotating though weapons in a
1237                first-person shooter. */
1238             if (0 < event->mouse_scroll.x_scroll && event->mouse_scroll.x_scroll < 120)
1239                 event->mouse_scroll.x_scroll = 120;
1240             else if (-120 < event->mouse_scroll.x_scroll && event->mouse_scroll.x_scroll < 0)
1241                 event->mouse_scroll.x_scroll = -120;
1242
1243             if (0 < event->mouse_scroll.y_scroll && event->mouse_scroll.y_scroll < 120)
1244                 event->mouse_scroll.y_scroll = 120;
1245             else if (-120 < event->mouse_scroll.y_scroll && event->mouse_scroll.y_scroll < 0)
1246                 event->mouse_scroll.y_scroll = -120;
1247         }
1248
1249         if (event->mouse_scroll.x_scroll || event->mouse_scroll.y_scroll)
1250             [queue postEvent:event];
1251
1252         macdrv_release_event(event);
1253     }
1254
1255
1256     /*
1257      * ---------- NSWindowDelegate methods ----------
1258      */
1259     - (void)windowDidBecomeKey:(NSNotification *)notification
1260     {
1261         WineApplicationController* controller = [WineApplicationController sharedController];
1262         NSEvent* event = [controller lastFlagsChanged];
1263         if (event)
1264             [self flagsChanged:event];
1265
1266         if (causing_becomeKeyWindow) return;
1267
1268         [controller windowGotFocus:self];
1269     }
1270
1271     - (void)windowDidDeminiaturize:(NSNotification *)notification
1272     {
1273         if (!ignore_windowDeminiaturize)
1274         {
1275             macdrv_event* event;
1276
1277             /* Coalesce events by discarding any previous ones still in the queue. */
1278             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1279                                              event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1280                                    forWindow:self];
1281
1282             event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1283             [queue postEvent:event];
1284             macdrv_release_event(event);
1285         }
1286
1287         ignore_windowDeminiaturize = FALSE;
1288
1289         [[WineApplicationController sharedController] wineWindow:self ordered:NSWindowAbove relativeTo:nil];
1290     }
1291
1292     - (void) windowDidEndLiveResize:(NSNotification *)notification
1293     {
1294         [liveResizeDisplayTimer invalidate];
1295         [liveResizeDisplayTimer release];
1296         liveResizeDisplayTimer = nil;
1297     }
1298
1299     - (void)windowDidMove:(NSNotification *)notification
1300     {
1301         [self windowDidResize:notification];
1302     }
1303
1304     - (void)windowDidResignKey:(NSNotification *)notification
1305     {
1306         macdrv_event* event;
1307
1308         if (causing_becomeKeyWindow) return;
1309
1310         event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1311         [queue postEvent:event];
1312         macdrv_release_event(event);
1313     }
1314
1315     - (void)windowDidResize:(NSNotification *)notification
1316     {
1317         macdrv_event* event;
1318         NSRect frame = [self contentRectForFrameRect:[self frame]];
1319
1320         [[WineApplicationController sharedController] flipRect:&frame];
1321
1322         /* Coalesce events by discarding any previous ones still in the queue. */
1323         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1324                                forWindow:self];
1325
1326         event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
1327         event->window_frame_changed.frame = NSRectToCGRect(frame);
1328         [queue postEvent:event];
1329         macdrv_release_event(event);
1330     }
1331
1332     - (BOOL)windowShouldClose:(id)sender
1333     {
1334         macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
1335         [queue postEvent:event];
1336         macdrv_release_event(event);
1337         return NO;
1338     }
1339
1340     - (void)windowWillMiniaturize:(NSNotification *)notification
1341     {
1342         if (!ignore_windowMiniaturize)
1343         {
1344             macdrv_event* event;
1345
1346             /* Coalesce events by discarding any previous ones still in the queue. */
1347             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1348                                              event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1349                                    forWindow:self];
1350
1351             event = macdrv_create_event(WINDOW_DID_MINIMIZE, self);
1352             [queue postEvent:event];
1353             macdrv_release_event(event);
1354         }
1355
1356         ignore_windowMiniaturize = FALSE;
1357     }
1358
1359     - (void) windowWillStartLiveResize:(NSNotification *)notification
1360     {
1361         // There's a strange restriction in window redrawing during Cocoa-
1362         // managed window resizing.  Only calls to -[NSView setNeedsDisplay...]
1363         // that happen synchronously when Cocoa tells us that our window size
1364         // has changed or asynchronously in a short interval thereafter provoke
1365         // the window to redraw.  Calls to those methods that happen asynchronously
1366         // a half second or more after the last change of the window size aren't
1367         // heeded until the next resize-related user event (e.g. mouse movement).
1368         //
1369         // Wine often has a significant delay between when it's been told that
1370         // the window has changed size and when it can flush completed drawing.
1371         // So, our windows would get stuck with incomplete drawing for as long
1372         // as the user holds the mouse button down and doesn't move it.
1373         //
1374         // We address this by "manually" asking our windows to check if they need
1375         // redrawing every so often (during live resize only).
1376         [self windowDidEndLiveResize:nil];
1377         liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
1378                                                                   target:self
1379                                                                 selector:@selector(displayIfNeeded)
1380                                                                 userInfo:nil
1381                                                                  repeats:YES];
1382         [liveResizeDisplayTimer retain];
1383         [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
1384                                      forMode:NSRunLoopCommonModes];
1385     }
1386
1387
1388     /*
1389      * ---------- NSPasteboardOwner methods ----------
1390      */
1391     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
1392     {
1393         macdrv_query* query = macdrv_create_query();
1394         query->type = QUERY_PASTEBOARD_DATA;
1395         query->window = (macdrv_window)[self retain];
1396         query->pasteboard_data.type = (CFStringRef)[type copy];
1397
1398         [self.queue query:query timeout:3];
1399         macdrv_release_query(query);
1400     }
1401
1402
1403     /*
1404      * ---------- NSDraggingDestination methods ----------
1405      */
1406     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
1407     {
1408         return [self draggingUpdated:sender];
1409     }
1410
1411     - (void) draggingExited:(id <NSDraggingInfo>)sender
1412     {
1413         // This isn't really a query.  We don't need any response.  However, it
1414         // has to be processed in a similar manner as the other drag-and-drop
1415         // queries in order to maintain the proper order of operations.
1416         macdrv_query* query = macdrv_create_query();
1417         query->type = QUERY_DRAG_EXITED;
1418         query->window = (macdrv_window)[self retain];
1419
1420         [self.queue query:query timeout:0.1];
1421         macdrv_release_query(query);
1422     }
1423
1424     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
1425     {
1426         NSDragOperation ret;
1427         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1428         NSPasteboard* pb = [sender draggingPasteboard];
1429
1430         macdrv_query* query = macdrv_create_query();
1431         query->type = QUERY_DRAG_OPERATION;
1432         query->window = (macdrv_window)[self retain];
1433         query->drag_operation.x = pt.x;
1434         query->drag_operation.y = pt.y;
1435         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
1436         query->drag_operation.accepted_op = NSDragOperationNone;
1437         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
1438
1439         [self.queue query:query timeout:3];
1440         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
1441         macdrv_release_query(query);
1442
1443         return ret;
1444     }
1445
1446     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
1447     {
1448         BOOL ret;
1449         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1450         NSPasteboard* pb = [sender draggingPasteboard];
1451
1452         macdrv_query* query = macdrv_create_query();
1453         query->type = QUERY_DRAG_DROP;
1454         query->window = (macdrv_window)[self retain];
1455         query->drag_drop.x = pt.x;
1456         query->drag_drop.y = pt.y;
1457         query->drag_drop.op = [sender draggingSourceOperationMask];
1458         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
1459
1460         [self.queue query:query timeout:3 * 60 processEvents:YES];
1461         ret = query->status;
1462         macdrv_release_query(query);
1463
1464         return ret;
1465     }
1466
1467     - (BOOL) wantsPeriodicDraggingUpdates
1468     {
1469         return NO;
1470     }
1471
1472 @end
1473
1474
1475 /***********************************************************************
1476  *              macdrv_create_cocoa_window
1477  *
1478  * Create a Cocoa window with the given content frame and features (e.g.
1479  * title bar, close box, etc.).
1480  */
1481 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
1482         CGRect frame, void* hwnd, macdrv_event_queue queue)
1483 {
1484     __block WineWindow* window;
1485
1486     OnMainThread(^{
1487         window = [[WineWindow createWindowWithFeatures:wf
1488                                            windowFrame:NSRectFromCGRect(frame)
1489                                                   hwnd:hwnd
1490                                                  queue:(WineEventQueue*)queue] retain];
1491     });
1492
1493     return (macdrv_window)window;
1494 }
1495
1496 /***********************************************************************
1497  *              macdrv_destroy_cocoa_window
1498  *
1499  * Destroy a Cocoa window.
1500  */
1501 void macdrv_destroy_cocoa_window(macdrv_window w)
1502 {
1503     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1504     WineWindow* window = (WineWindow*)w;
1505
1506     [window.queue discardEventsMatchingMask:-1 forWindow:window];
1507     [window close];
1508     [window release];
1509
1510     [pool release];
1511 }
1512
1513 /***********************************************************************
1514  *              macdrv_get_window_hwnd
1515  *
1516  * Get the hwnd that was set for the window at creation.
1517  */
1518 void* macdrv_get_window_hwnd(macdrv_window w)
1519 {
1520     WineWindow* window = (WineWindow*)w;
1521     return window.hwnd;
1522 }
1523
1524 /***********************************************************************
1525  *              macdrv_set_cocoa_window_features
1526  *
1527  * Update a Cocoa window's features.
1528  */
1529 void macdrv_set_cocoa_window_features(macdrv_window w,
1530         const struct macdrv_window_features* wf)
1531 {
1532     WineWindow* window = (WineWindow*)w;
1533
1534     OnMainThread(^{
1535         [window setWindowFeatures:wf];
1536     });
1537 }
1538
1539 /***********************************************************************
1540  *              macdrv_set_cocoa_window_state
1541  *
1542  * Update a Cocoa window's state.
1543  */
1544 void macdrv_set_cocoa_window_state(macdrv_window w,
1545         const struct macdrv_window_state* state)
1546 {
1547     WineWindow* window = (WineWindow*)w;
1548
1549     OnMainThread(^{
1550         [window setMacDrvState:state];
1551     });
1552 }
1553
1554 /***********************************************************************
1555  *              macdrv_set_cocoa_window_title
1556  *
1557  * Set a Cocoa window's title.
1558  */
1559 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
1560         size_t length)
1561 {
1562     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1563     WineWindow* window = (WineWindow*)w;
1564     NSString* titleString;
1565
1566     if (title)
1567         titleString = [NSString stringWithCharacters:title length:length];
1568     else
1569         titleString = @"";
1570     OnMainThreadAsync(^{
1571         [window setTitle:titleString];
1572         if ([window isVisible] && ![window isExcludedFromWindowsMenu])
1573             [NSApp changeWindowsItem:window title:titleString filename:NO];
1574     });
1575
1576     [pool release];
1577 }
1578
1579 /***********************************************************************
1580  *              macdrv_order_cocoa_window
1581  *
1582  * Reorder a Cocoa window relative to other windows.  If prev is
1583  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
1584  * it is ordered above that window.  Otherwise, it is ordered to the
1585  * front.
1586  *
1587  * Returns true if the window has actually been ordered onto the screen
1588  * (i.e. if its frame intersects with a screen).  Otherwise, false.
1589  */
1590 int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
1591         macdrv_window next)
1592 {
1593     WineWindow* window = (WineWindow*)w;
1594     __block BOOL on_screen;
1595
1596     OnMainThread(^{
1597         on_screen = [window orderBelow:(WineWindow*)prev
1598                                orAbove:(WineWindow*)next];
1599     });
1600
1601     return on_screen;
1602 }
1603
1604 /***********************************************************************
1605  *              macdrv_hide_cocoa_window
1606  *
1607  * Hides a Cocoa window.
1608  */
1609 void macdrv_hide_cocoa_window(macdrv_window w)
1610 {
1611     WineWindow* window = (WineWindow*)w;
1612
1613     OnMainThread(^{
1614         [window doOrderOut];
1615     });
1616 }
1617
1618 /***********************************************************************
1619  *              macdrv_set_cocoa_window_frame
1620  *
1621  * Move a Cocoa window.  If the window has been moved out of the bounds
1622  * of the desktop, it is ordered out.  (This routine won't ever order a
1623  * window in, though.)
1624  *
1625  * Returns true if the window is on screen; false otherwise.
1626  */
1627 int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
1628 {
1629     WineWindow* window = (WineWindow*)w;
1630     __block BOOL on_screen;
1631
1632     OnMainThread(^{
1633         on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)];
1634     });
1635
1636     return on_screen;
1637 }
1638
1639 /***********************************************************************
1640  *              macdrv_get_cocoa_window_frame
1641  *
1642  * Gets the frame of a Cocoa window.
1643  */
1644 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
1645 {
1646     WineWindow* window = (WineWindow*)w;
1647
1648     OnMainThread(^{
1649         NSRect frame;
1650
1651         frame = [window contentRectForFrameRect:[window frame]];
1652         [[WineApplicationController sharedController] flipRect:&frame];
1653         *out_frame = NSRectToCGRect(frame);
1654     });
1655 }
1656
1657 /***********************************************************************
1658  *              macdrv_set_cocoa_parent_window
1659  *
1660  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
1661  * the parent window.
1662  */
1663 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
1664 {
1665     WineWindow* window = (WineWindow*)w;
1666
1667     OnMainThread(^{
1668         [window setMacDrvParentWindow:(WineWindow*)parent];
1669     });
1670 }
1671
1672 /***********************************************************************
1673  *              macdrv_set_window_surface
1674  */
1675 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
1676 {
1677     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1678     WineWindow* window = (WineWindow*)w;
1679
1680     OnMainThread(^{
1681         window.surface = surface;
1682         window.surface_mutex = mutex;
1683     });
1684
1685     [pool release];
1686 }
1687
1688 /***********************************************************************
1689  *              macdrv_window_needs_display
1690  *
1691  * Mark a window as needing display in a specified rect (in non-client
1692  * area coordinates).
1693  */
1694 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
1695 {
1696     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1697     WineWindow* window = (WineWindow*)w;
1698
1699     OnMainThreadAsync(^{
1700         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
1701     });
1702
1703     [pool release];
1704 }
1705
1706 /***********************************************************************
1707  *              macdrv_set_window_shape
1708  *
1709  * Sets the shape of a Cocoa window from an array of rectangles.  If
1710  * rects is NULL, resets the window's shape to its frame.
1711  */
1712 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
1713 {
1714     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1715     WineWindow* window = (WineWindow*)w;
1716
1717     OnMainThread(^{
1718         if (!rects || !count)
1719             window.shape = nil;
1720         else
1721         {
1722             NSBezierPath* path;
1723             unsigned int i;
1724
1725             path = [NSBezierPath bezierPath];
1726             for (i = 0; i < count; i++)
1727                 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
1728             window.shape = path;
1729         }
1730     });
1731
1732     [pool release];
1733 }
1734
1735 /***********************************************************************
1736  *              macdrv_set_window_alpha
1737  */
1738 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
1739 {
1740     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1741     WineWindow* window = (WineWindow*)w;
1742
1743     [window setAlphaValue:alpha];
1744
1745     [pool release];
1746 }
1747
1748 /***********************************************************************
1749  *              macdrv_set_window_color_key
1750  */
1751 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
1752                                  CGFloat keyBlue)
1753 {
1754     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1755     WineWindow* window = (WineWindow*)w;
1756
1757     OnMainThread(^{
1758         window.colorKeyed       = TRUE;
1759         window.colorKeyRed      = keyRed;
1760         window.colorKeyGreen    = keyGreen;
1761         window.colorKeyBlue     = keyBlue;
1762         [window checkTransparency];
1763     });
1764
1765     [pool release];
1766 }
1767
1768 /***********************************************************************
1769  *              macdrv_clear_window_color_key
1770  */
1771 void macdrv_clear_window_color_key(macdrv_window w)
1772 {
1773     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1774     WineWindow* window = (WineWindow*)w;
1775
1776     OnMainThread(^{
1777         window.colorKeyed = FALSE;
1778         [window checkTransparency];
1779     });
1780
1781     [pool release];
1782 }
1783
1784 /***********************************************************************
1785  *              macdrv_window_use_per_pixel_alpha
1786  */
1787 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
1788 {
1789     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1790     WineWindow* window = (WineWindow*)w;
1791
1792     OnMainThread(^{
1793         window.usePerPixelAlpha = use_per_pixel_alpha;
1794         [window checkTransparency];
1795     });
1796
1797     [pool release];
1798 }
1799
1800 /***********************************************************************
1801  *              macdrv_give_cocoa_window_focus
1802  *
1803  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
1804  * orders it front and, if its frame was not within the desktop bounds,
1805  * Cocoa will typically move it on-screen.
1806  */
1807 void macdrv_give_cocoa_window_focus(macdrv_window w)
1808 {
1809     WineWindow* window = (WineWindow*)w;
1810
1811     OnMainThread(^{
1812         [window makeFocused];
1813     });
1814 }
1815
1816 /***********************************************************************
1817  *              macdrv_create_view
1818  *
1819  * Creates and returns a view in the specified rect of the window.  The
1820  * caller is responsible for calling macdrv_dispose_view() on the view
1821  * when it is done with it.
1822  */
1823 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
1824 {
1825     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1826     WineWindow* window = (WineWindow*)w;
1827     __block WineContentView* view;
1828
1829     if (CGRectIsNull(rect)) rect = CGRectZero;
1830
1831     OnMainThread(^{
1832         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1833
1834         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
1835         [view setAutoresizesSubviews:NO];
1836         [nc addObserver:view
1837                selector:@selector(updateGLContexts)
1838                    name:NSViewGlobalFrameDidChangeNotification
1839                  object:view];
1840         [nc addObserver:view
1841                selector:@selector(updateGLContexts)
1842                    name:NSApplicationDidChangeScreenParametersNotification
1843                  object:NSApp];
1844         [[window contentView] addSubview:view];
1845     });
1846
1847     [pool release];
1848     return (macdrv_view)view;
1849 }
1850
1851 /***********************************************************************
1852  *              macdrv_dispose_view
1853  *
1854  * Destroys a view previously returned by macdrv_create_view.
1855  */
1856 void macdrv_dispose_view(macdrv_view v)
1857 {
1858     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1859     WineContentView* view = (WineContentView*)v;
1860
1861     OnMainThread(^{
1862         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1863
1864         [nc removeObserver:view
1865                       name:NSViewGlobalFrameDidChangeNotification
1866                     object:view];
1867         [nc removeObserver:view
1868                       name:NSApplicationDidChangeScreenParametersNotification
1869                     object:NSApp];
1870         [view removeFromSuperview];
1871         [view release];
1872     });
1873
1874     [pool release];
1875 }
1876
1877 /***********************************************************************
1878  *              macdrv_set_view_window_and_frame
1879  *
1880  * Move a view to a new window and/or position within its window.  If w
1881  * is NULL, leave the view in its current window and just change its
1882  * frame.
1883  */
1884 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
1885 {
1886     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1887     WineContentView* view = (WineContentView*)v;
1888     WineWindow* window = (WineWindow*)w;
1889
1890     if (CGRectIsNull(rect)) rect = CGRectZero;
1891
1892     OnMainThread(^{
1893         BOOL changedWindow = (window && window != [view window]);
1894         NSRect newFrame = NSRectFromCGRect(rect);
1895         NSRect oldFrame = [view frame];
1896
1897         if (changedWindow)
1898         {
1899             [view removeFromSuperview];
1900             [[window contentView] addSubview:view];
1901         }
1902
1903         if (!NSEqualRects(oldFrame, newFrame))
1904         {
1905             if (!changedWindow)
1906                 [[view superview] setNeedsDisplayInRect:oldFrame];
1907             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
1908                 [view setFrameSize:newFrame.size];
1909             else if (NSEqualSizes(oldFrame.size, newFrame.size))
1910                 [view setFrameOrigin:newFrame.origin];
1911             else
1912                 [view setFrame:newFrame];
1913             [view setNeedsDisplay:YES];
1914         }
1915     });
1916
1917     [pool release];
1918 }
1919
1920 /***********************************************************************
1921  *              macdrv_add_view_opengl_context
1922  *
1923  * Add an OpenGL context to the list being tracked for each view.
1924  */
1925 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
1926 {
1927     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1928     WineContentView* view = (WineContentView*)v;
1929     WineOpenGLContext *context = (WineOpenGLContext*)c;
1930
1931     OnMainThreadAsync(^{
1932         [view addGLContext:context];
1933     });
1934
1935     [pool release];
1936 }
1937
1938 /***********************************************************************
1939  *              macdrv_remove_view_opengl_context
1940  *
1941  * Add an OpenGL context to the list being tracked for each view.
1942  */
1943 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
1944 {
1945     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1946     WineContentView* view = (WineContentView*)v;
1947     WineOpenGLContext *context = (WineOpenGLContext*)c;
1948
1949     OnMainThreadAsync(^{
1950         [view removeGLContext:context];
1951     });
1952
1953     [pool release];
1954 }
1955
1956 /***********************************************************************
1957  *              macdrv_window_background_color
1958  *
1959  * Returns the standard Mac window background color as a 32-bit value of
1960  * the form 0x00rrggbb.
1961  */
1962 uint32_t macdrv_window_background_color(void)
1963 {
1964     static uint32_t result;
1965     static dispatch_once_t once;
1966
1967     // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
1968     // color spaces (RGB or grayscale).  So, the only way to get RGB values out
1969     // of it is to draw with it.
1970     dispatch_once(&once, ^{
1971         OnMainThread(^{
1972             unsigned char rgbx[4];
1973             unsigned char *planes = rgbx;
1974             NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
1975                                                                                pixelsWide:1
1976                                                                                pixelsHigh:1
1977                                                                             bitsPerSample:8
1978                                                                           samplesPerPixel:3
1979                                                                                  hasAlpha:NO
1980                                                                                  isPlanar:NO
1981                                                                            colorSpaceName:NSCalibratedRGBColorSpace
1982                                                                              bitmapFormat:0
1983                                                                               bytesPerRow:4
1984                                                                              bitsPerPixel:32];
1985             [NSGraphicsContext saveGraphicsState];
1986             [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
1987             [[NSColor windowBackgroundColor] set];
1988             NSRectFill(NSMakeRect(0, 0, 1, 1));
1989             [NSGraphicsContext restoreGraphicsState];
1990             [bitmap release];
1991             result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
1992         });
1993     });
1994
1995     return result;
1996 }
1997
1998 /***********************************************************************
1999  *              macdrv_send_text_input_event
2000  */
2001 int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data)
2002 {
2003     __block BOOL ret;
2004
2005     OnMainThread(^{
2006         WineWindow* window = (WineWindow*)[NSApp keyWindow];
2007         if (![window isKindOfClass:[WineWindow class]])
2008         {
2009             window = (WineWindow*)[NSApp mainWindow];
2010             if (![window isKindOfClass:[WineWindow class]] && [[NSApp orderedWineWindows] count])
2011             {
2012                 window = [[NSApp orderedWineWindows] objectAtIndex:0];
2013                 if (![window isKindOfClass:[WineWindow class]])
2014                     window = nil;
2015             }
2016         }
2017
2018         if (window)
2019         {
2020             NSUInteger localFlags = flags;
2021             CGEventRef c;
2022             NSEvent* event;
2023
2024             window.imeData = data;
2025             fix_device_modifiers_by_generic(&localFlags);
2026
2027             // An NSEvent created with +keyEventWithType:... is internally marked
2028             // as synthetic and doesn't get sent through input methods.  But one
2029             // created from a CGEvent doesn't have that problem.
2030             c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
2031             CGEventSetFlags(c, localFlags);
2032             CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
2033             event = [NSEvent eventWithCGEvent:c];
2034             CFRelease(c);
2035
2036             window.commandDone = FALSE;
2037             ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
2038         }
2039         else
2040             ret = FALSE;
2041     });
2042
2043     return ret;
2044 }