Release 1.5.29.
[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:(BOOL)activate
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         if (activate)
893             [NSApp activateIgnoringOtherApps:YES];
894         [self orderFront:nil];
895         [controller wineWindow:self ordered:NSWindowAbove relativeTo:nil];
896         causing_becomeKeyWindow = TRUE;
897         [self makeKeyWindow];
898         causing_becomeKeyWindow = FALSE;
899         if (latentParentWindow)
900         {
901             if ([latentParentWindow level] > [self level])
902                 [self setLevelWhenActive:[latentParentWindow level]];
903             [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
904             [controller wineWindow:self ordered:NSWindowAbove relativeTo:latentParentWindow];
905             self.latentParentWindow = nil;
906         }
907         if (![self isExcludedFromWindowsMenu])
908             [NSApp addWindowsItem:self title:[self title] filename:NO];
909
910         /* Cocoa may adjust the frame when the window is ordered onto the screen.
911            Generate a frame-changed event just in case.  The back end will ignore
912            it if nothing actually changed. */
913         [self windowDidResize:nil];
914     }
915
916     - (void) postKey:(uint16_t)keyCode
917              pressed:(BOOL)pressed
918            modifiers:(NSUInteger)modifiers
919                event:(NSEvent*)theEvent
920     {
921         macdrv_event* event;
922         CGEventRef cgevent;
923         WineApplicationController* controller = [WineApplicationController sharedController];
924
925         event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
926         event->key.keycode   = keyCode;
927         event->key.modifiers = modifiers;
928         event->key.time_ms   = [controller ticksForEventTime:[theEvent timestamp]];
929
930         if ((cgevent = [theEvent CGEvent]))
931         {
932             CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
933                                                         kCGKeyboardEventKeyboardType);
934             if (keyboardType != controller.keyboardType)
935             {
936                 controller.keyboardType = keyboardType;
937                 [controller keyboardSelectionDidChange];
938             }
939         }
940
941         [queue postEvent:event];
942
943         macdrv_release_event(event);
944     }
945
946     - (void) postKeyEvent:(NSEvent *)theEvent
947     {
948         [self flagsChanged:theEvent];
949         [self postKey:[theEvent keyCode]
950               pressed:[theEvent type] == NSKeyDown
951             modifiers:[theEvent modifierFlags]
952                 event:theEvent];
953     }
954
955     - (void) postMouseMovedEvent:(NSEvent *)theEvent absolute:(BOOL)absolute
956     {
957         macdrv_event* event;
958
959         if (absolute)
960         {
961             CGPoint point = CGEventGetLocation([theEvent CGEvent]);
962
963             event = macdrv_create_event(MOUSE_MOVED_ABSOLUTE, self);
964             event->mouse_moved.x = point.x;
965             event->mouse_moved.y = point.y;
966
967             mouseMoveDeltaX = 0;
968             mouseMoveDeltaY = 0;
969         }
970         else
971         {
972             /* Add event delta to accumulated delta error */
973             /* deltaY is already flipped */
974             mouseMoveDeltaX += [theEvent deltaX];
975             mouseMoveDeltaY += [theEvent deltaY];
976
977             event = macdrv_create_event(MOUSE_MOVED, self);
978             event->mouse_moved.x = mouseMoveDeltaX;
979             event->mouse_moved.y = mouseMoveDeltaY;
980
981             /* Keep the remainder after integer truncation. */
982             mouseMoveDeltaX -= event->mouse_moved.x;
983             mouseMoveDeltaY -= event->mouse_moved.y;
984         }
985
986         if (event->type == MOUSE_MOVED_ABSOLUTE || event->mouse_moved.x || event->mouse_moved.y)
987         {
988             event->mouse_moved.time_ms = [[WineApplicationController sharedController] ticksForEventTime:[theEvent timestamp]];
989
990             [queue postEvent:event];
991         }
992
993         macdrv_release_event(event);
994     }
995
996     - (void) setLevelWhenActive:(NSInteger)level
997     {
998         levelWhenActive = level;
999         if (([NSApp isActive] || level <= NSFloatingWindowLevel) &&
1000             level != [self level])
1001             [self setLevel:level];
1002     }
1003
1004
1005     /*
1006      * ---------- NSWindow method overrides ----------
1007      */
1008     - (BOOL) canBecomeKeyWindow
1009     {
1010         if (causing_becomeKeyWindow) return YES;
1011         if (self.disabled || self.noActivate) return NO;
1012         return [self isKeyWindow];
1013     }
1014
1015     - (BOOL) canBecomeMainWindow
1016     {
1017         return [self canBecomeKeyWindow];
1018     }
1019
1020     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
1021     {
1022         // If a window is sized to completely cover a screen, then it's in
1023         // full-screen mode.  In that case, we don't allow NSWindow to constrain
1024         // it.
1025         NSRect contentRect = [self contentRectForFrameRect:frameRect];
1026         if (!screen_covered_by_rect(contentRect, [NSScreen screens]))
1027             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
1028         return frameRect;
1029     }
1030
1031     - (BOOL) isExcludedFromWindowsMenu
1032     {
1033         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
1034     }
1035
1036     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
1037     {
1038         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
1039             return [self isKeyWindow] || (!self.disabled && !self.noActivate);
1040         return [super validateMenuItem:menuItem];
1041     }
1042
1043     /* We don't call this.  It's the action method of the items in the Window menu. */
1044     - (void) makeKeyAndOrderFront:(id)sender
1045     {
1046         if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1047             [[WineApplicationController sharedController] windowGotFocus:self];
1048     }
1049
1050     - (void) sendEvent:(NSEvent*)event
1051     {
1052         WineApplicationController* controller = [WineApplicationController sharedController];
1053
1054         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
1055            interface control.  For example, Control-Tab switches focus among
1056            views.  We want to bypass that feature, so directly route key-down
1057            events to -keyDown:. */
1058         if ([event type] == NSKeyDown)
1059             [[self firstResponder] keyDown:event];
1060         else
1061         {
1062             if ([event type] == NSLeftMouseDown)
1063             {
1064                 NSWindowButton windowButton;
1065                 BOOL broughtWindowForward = TRUE;
1066
1067                 /* Since our windows generally claim they can't be made key, clicks
1068                    in their title bars are swallowed by the theme frame stuff.  So,
1069                    we hook directly into the event stream and assume that any click
1070                    in the window will activate it, if Wine and the Win32 program
1071                    accept. */
1072                 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1073                     [controller windowGotFocus:self];
1074
1075                 /* Any left-click on our window anyplace other than the close or
1076                    minimize buttons will bring it forward. */
1077                 for (windowButton = NSWindowCloseButton;
1078                      windowButton <= NSWindowMiniaturizeButton;
1079                      windowButton++)
1080                 {
1081                     NSButton* button = [[event window] standardWindowButton:windowButton];
1082                     if (button)
1083                     {
1084                         NSPoint point = [button convertPoint:[event locationInWindow] fromView:nil];
1085                         if ([button mouse:point inRect:[button bounds]])
1086                         {
1087                             broughtWindowForward = FALSE;
1088                             break;
1089                         }
1090                     }
1091                 }
1092
1093                 if (broughtWindowForward)
1094                     [controller wineWindow:self ordered:NSWindowAbove relativeTo:nil];
1095             }
1096
1097             [super sendEvent:event];
1098         }
1099     }
1100
1101
1102     /*
1103      * ---------- NSResponder method overrides ----------
1104      */
1105     - (void) mouseDown:(NSEvent *)theEvent { [self postMouseButtonEvent:theEvent pressed:1]; }
1106     - (void) rightMouseDown:(NSEvent *)theEvent { [self mouseDown:theEvent]; }
1107     - (void) otherMouseDown:(NSEvent *)theEvent { [self mouseDown:theEvent]; }
1108
1109     - (void) mouseUp:(NSEvent *)theEvent { [self postMouseButtonEvent:theEvent pressed:0]; }
1110     - (void) rightMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
1111     - (void) otherMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
1112
1113     - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1114     - (void) keyUp:(NSEvent *)theEvent   { [self postKeyEvent:theEvent]; }
1115
1116     - (void) flagsChanged:(NSEvent *)theEvent
1117     {
1118         static const struct {
1119             NSUInteger  mask;
1120             uint16_t    keycode;
1121         } modifiers[] = {
1122             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
1123             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
1124             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
1125             { NX_DEVICELCTLKEYMASK,     kVK_Control },
1126             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
1127             { NX_DEVICELALTKEYMASK,     kVK_Option },
1128             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
1129             { NX_DEVICELCMDKEYMASK,     kVK_Command },
1130             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
1131         };
1132
1133         NSUInteger modifierFlags = [theEvent modifierFlags];
1134         NSUInteger changed;
1135         int i, last_changed;
1136
1137         fix_device_modifiers_by_generic(&modifierFlags);
1138         changed = modifierFlags ^ lastModifierFlags;
1139
1140         last_changed = -1;
1141         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
1142             if (changed & modifiers[i].mask)
1143                 last_changed = i;
1144
1145         for (i = 0; i <= last_changed; i++)
1146         {
1147             if (changed & modifiers[i].mask)
1148             {
1149                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
1150
1151                 if (i == last_changed)
1152                     lastModifierFlags = modifierFlags;
1153                 else
1154                 {
1155                     lastModifierFlags ^= modifiers[i].mask;
1156                     fix_generic_modifiers_by_device(&lastModifierFlags);
1157                 }
1158
1159                 // Caps lock generates one event for each press-release action.
1160                 // We need to simulate a pair of events for each actual event.
1161                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1162                 {
1163                     [self postKey:modifiers[i].keycode
1164                           pressed:TRUE
1165                         modifiers:lastModifierFlags
1166                             event:(NSEvent*)theEvent];
1167                     pressed = FALSE;
1168                 }
1169
1170                 [self postKey:modifiers[i].keycode
1171                       pressed:pressed
1172                     modifiers:lastModifierFlags
1173                         event:(NSEvent*)theEvent];
1174             }
1175         }
1176     }
1177
1178     - (void) scrollWheel:(NSEvent *)theEvent
1179     {
1180         CGPoint pt;
1181         macdrv_event* event;
1182         CGEventRef cgevent;
1183         CGFloat x, y;
1184         BOOL continuous = FALSE;
1185
1186         cgevent = [theEvent CGEvent];
1187         pt = CGEventGetLocation(cgevent);
1188
1189         event = macdrv_create_event(MOUSE_SCROLL, self);
1190         event->mouse_scroll.x = pt.x;
1191         event->mouse_scroll.y = pt.y;
1192         event->mouse_scroll.time_ms = [[WineApplicationController sharedController] ticksForEventTime:[theEvent timestamp]];
1193
1194         if (CGEventGetIntegerValueField(cgevent, kCGScrollWheelEventIsContinuous))
1195         {
1196             continuous = TRUE;
1197
1198             /* Continuous scroll wheel events come from high-precision scrolling
1199                hardware like Apple's Magic Mouse, Mighty Mouse, and trackpads.
1200                For these, we can get more precise data from the CGEvent API. */
1201             /* Axis 1 is vertical, axis 2 is horizontal. */
1202             x = CGEventGetDoubleValueField(cgevent, kCGScrollWheelEventPointDeltaAxis2);
1203             y = CGEventGetDoubleValueField(cgevent, kCGScrollWheelEventPointDeltaAxis1);
1204         }
1205         else
1206         {
1207             double pixelsPerLine = 10;
1208             CGEventSourceRef source;
1209
1210             /* The non-continuous values are in units of "lines", not pixels. */
1211             if ((source = CGEventCreateSourceFromEvent(cgevent)))
1212             {
1213                 pixelsPerLine = CGEventSourceGetPixelsPerLine(source);
1214                 CFRelease(source);
1215             }
1216
1217             x = pixelsPerLine * [theEvent deltaX];
1218             y = pixelsPerLine * [theEvent deltaY];
1219         }
1220
1221         /* Mac: negative is right or down, positive is left or up.
1222            Win32: negative is left or down, positive is right or up.
1223            So, negate the X scroll value to translate. */
1224         x = -x;
1225
1226         /* The x,y values so far are in pixels.  Win32 expects to receive some
1227            fraction of WHEEL_DELTA == 120.  By my estimation, that's roughly
1228            6 times the pixel value. */
1229         event->mouse_scroll.x_scroll = 6 * x;
1230         event->mouse_scroll.y_scroll = 6 * y;
1231
1232         if (!continuous)
1233         {
1234             /* For non-continuous "clicky" wheels, if there was any motion, make
1235                sure there was at least WHEEL_DELTA motion.  This is so, at slow
1236                speeds where the system's acceleration curve is actually reducing the
1237                scroll distance, the user is sure to get some action out of each click.
1238                For example, this is important for rotating though weapons in a
1239                first-person shooter. */
1240             if (0 < event->mouse_scroll.x_scroll && event->mouse_scroll.x_scroll < 120)
1241                 event->mouse_scroll.x_scroll = 120;
1242             else if (-120 < event->mouse_scroll.x_scroll && event->mouse_scroll.x_scroll < 0)
1243                 event->mouse_scroll.x_scroll = -120;
1244
1245             if (0 < event->mouse_scroll.y_scroll && event->mouse_scroll.y_scroll < 120)
1246                 event->mouse_scroll.y_scroll = 120;
1247             else if (-120 < event->mouse_scroll.y_scroll && event->mouse_scroll.y_scroll < 0)
1248                 event->mouse_scroll.y_scroll = -120;
1249         }
1250
1251         if (event->mouse_scroll.x_scroll || event->mouse_scroll.y_scroll)
1252             [queue postEvent:event];
1253
1254         macdrv_release_event(event);
1255     }
1256
1257
1258     /*
1259      * ---------- NSWindowDelegate methods ----------
1260      */
1261     - (void)windowDidBecomeKey:(NSNotification *)notification
1262     {
1263         WineApplicationController* controller = [WineApplicationController sharedController];
1264         NSEvent* event = [controller lastFlagsChanged];
1265         if (event)
1266             [self flagsChanged:event];
1267
1268         if (causing_becomeKeyWindow) return;
1269
1270         [controller windowGotFocus:self];
1271     }
1272
1273     - (void)windowDidDeminiaturize:(NSNotification *)notification
1274     {
1275         if (!ignore_windowDeminiaturize)
1276         {
1277             macdrv_event* event;
1278
1279             /* Coalesce events by discarding any previous ones still in the queue. */
1280             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1281                                              event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1282                                    forWindow:self];
1283
1284             event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1285             [queue postEvent:event];
1286             macdrv_release_event(event);
1287         }
1288
1289         ignore_windowDeminiaturize = FALSE;
1290
1291         [[WineApplicationController sharedController] wineWindow:self ordered:NSWindowAbove relativeTo:nil];
1292     }
1293
1294     - (void) windowDidEndLiveResize:(NSNotification *)notification
1295     {
1296         [liveResizeDisplayTimer invalidate];
1297         [liveResizeDisplayTimer release];
1298         liveResizeDisplayTimer = nil;
1299     }
1300
1301     - (void)windowDidMove:(NSNotification *)notification
1302     {
1303         [self windowDidResize:notification];
1304     }
1305
1306     - (void)windowDidResignKey:(NSNotification *)notification
1307     {
1308         macdrv_event* event;
1309
1310         if (causing_becomeKeyWindow) return;
1311
1312         event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1313         [queue postEvent:event];
1314         macdrv_release_event(event);
1315     }
1316
1317     - (void)windowDidResize:(NSNotification *)notification
1318     {
1319         macdrv_event* event;
1320         NSRect frame = [self contentRectForFrameRect:[self frame]];
1321
1322         [[WineApplicationController sharedController] flipRect:&frame];
1323
1324         /* Coalesce events by discarding any previous ones still in the queue. */
1325         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1326                                forWindow:self];
1327
1328         event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
1329         event->window_frame_changed.frame = NSRectToCGRect(frame);
1330         [queue postEvent:event];
1331         macdrv_release_event(event);
1332     }
1333
1334     - (BOOL)windowShouldClose:(id)sender
1335     {
1336         macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
1337         [queue postEvent:event];
1338         macdrv_release_event(event);
1339         return NO;
1340     }
1341
1342     - (void)windowWillMiniaturize:(NSNotification *)notification
1343     {
1344         if (!ignore_windowMiniaturize)
1345         {
1346             macdrv_event* event;
1347
1348             /* Coalesce events by discarding any previous ones still in the queue. */
1349             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1350                                              event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1351                                    forWindow:self];
1352
1353             event = macdrv_create_event(WINDOW_DID_MINIMIZE, self);
1354             [queue postEvent:event];
1355             macdrv_release_event(event);
1356         }
1357
1358         ignore_windowMiniaturize = FALSE;
1359     }
1360
1361     - (void) windowWillStartLiveResize:(NSNotification *)notification
1362     {
1363         // There's a strange restriction in window redrawing during Cocoa-
1364         // managed window resizing.  Only calls to -[NSView setNeedsDisplay...]
1365         // that happen synchronously when Cocoa tells us that our window size
1366         // has changed or asynchronously in a short interval thereafter provoke
1367         // the window to redraw.  Calls to those methods that happen asynchronously
1368         // a half second or more after the last change of the window size aren't
1369         // heeded until the next resize-related user event (e.g. mouse movement).
1370         //
1371         // Wine often has a significant delay between when it's been told that
1372         // the window has changed size and when it can flush completed drawing.
1373         // So, our windows would get stuck with incomplete drawing for as long
1374         // as the user holds the mouse button down and doesn't move it.
1375         //
1376         // We address this by "manually" asking our windows to check if they need
1377         // redrawing every so often (during live resize only).
1378         [self windowDidEndLiveResize:nil];
1379         liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
1380                                                                   target:self
1381                                                                 selector:@selector(displayIfNeeded)
1382                                                                 userInfo:nil
1383                                                                  repeats:YES];
1384         [liveResizeDisplayTimer retain];
1385         [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
1386                                      forMode:NSRunLoopCommonModes];
1387     }
1388
1389
1390     /*
1391      * ---------- NSPasteboardOwner methods ----------
1392      */
1393     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
1394     {
1395         macdrv_query* query = macdrv_create_query();
1396         query->type = QUERY_PASTEBOARD_DATA;
1397         query->window = (macdrv_window)[self retain];
1398         query->pasteboard_data.type = (CFStringRef)[type copy];
1399
1400         [self.queue query:query timeout:3];
1401         macdrv_release_query(query);
1402     }
1403
1404
1405     /*
1406      * ---------- NSDraggingDestination methods ----------
1407      */
1408     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
1409     {
1410         return [self draggingUpdated:sender];
1411     }
1412
1413     - (void) draggingExited:(id <NSDraggingInfo>)sender
1414     {
1415         // This isn't really a query.  We don't need any response.  However, it
1416         // has to be processed in a similar manner as the other drag-and-drop
1417         // queries in order to maintain the proper order of operations.
1418         macdrv_query* query = macdrv_create_query();
1419         query->type = QUERY_DRAG_EXITED;
1420         query->window = (macdrv_window)[self retain];
1421
1422         [self.queue query:query timeout:0.1];
1423         macdrv_release_query(query);
1424     }
1425
1426     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
1427     {
1428         NSDragOperation ret;
1429         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1430         NSPasteboard* pb = [sender draggingPasteboard];
1431
1432         macdrv_query* query = macdrv_create_query();
1433         query->type = QUERY_DRAG_OPERATION;
1434         query->window = (macdrv_window)[self retain];
1435         query->drag_operation.x = pt.x;
1436         query->drag_operation.y = pt.y;
1437         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
1438         query->drag_operation.accepted_op = NSDragOperationNone;
1439         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
1440
1441         [self.queue query:query timeout:3];
1442         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
1443         macdrv_release_query(query);
1444
1445         return ret;
1446     }
1447
1448     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
1449     {
1450         BOOL ret;
1451         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1452         NSPasteboard* pb = [sender draggingPasteboard];
1453
1454         macdrv_query* query = macdrv_create_query();
1455         query->type = QUERY_DRAG_DROP;
1456         query->window = (macdrv_window)[self retain];
1457         query->drag_drop.x = pt.x;
1458         query->drag_drop.y = pt.y;
1459         query->drag_drop.op = [sender draggingSourceOperationMask];
1460         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
1461
1462         [self.queue query:query timeout:3 * 60 processEvents:YES];
1463         ret = query->status;
1464         macdrv_release_query(query);
1465
1466         return ret;
1467     }
1468
1469     - (BOOL) wantsPeriodicDraggingUpdates
1470     {
1471         return NO;
1472     }
1473
1474 @end
1475
1476
1477 /***********************************************************************
1478  *              macdrv_create_cocoa_window
1479  *
1480  * Create a Cocoa window with the given content frame and features (e.g.
1481  * title bar, close box, etc.).
1482  */
1483 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
1484         CGRect frame, void* hwnd, macdrv_event_queue queue)
1485 {
1486     __block WineWindow* window;
1487
1488     OnMainThread(^{
1489         window = [[WineWindow createWindowWithFeatures:wf
1490                                            windowFrame:NSRectFromCGRect(frame)
1491                                                   hwnd:hwnd
1492                                                  queue:(WineEventQueue*)queue] retain];
1493     });
1494
1495     return (macdrv_window)window;
1496 }
1497
1498 /***********************************************************************
1499  *              macdrv_destroy_cocoa_window
1500  *
1501  * Destroy a Cocoa window.
1502  */
1503 void macdrv_destroy_cocoa_window(macdrv_window w)
1504 {
1505     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1506     WineWindow* window = (WineWindow*)w;
1507
1508     [window.queue discardEventsMatchingMask:-1 forWindow:window];
1509     [window close];
1510     [window release];
1511
1512     [pool release];
1513 }
1514
1515 /***********************************************************************
1516  *              macdrv_get_window_hwnd
1517  *
1518  * Get the hwnd that was set for the window at creation.
1519  */
1520 void* macdrv_get_window_hwnd(macdrv_window w)
1521 {
1522     WineWindow* window = (WineWindow*)w;
1523     return window.hwnd;
1524 }
1525
1526 /***********************************************************************
1527  *              macdrv_set_cocoa_window_features
1528  *
1529  * Update a Cocoa window's features.
1530  */
1531 void macdrv_set_cocoa_window_features(macdrv_window w,
1532         const struct macdrv_window_features* wf)
1533 {
1534     WineWindow* window = (WineWindow*)w;
1535
1536     OnMainThread(^{
1537         [window setWindowFeatures:wf];
1538     });
1539 }
1540
1541 /***********************************************************************
1542  *              macdrv_set_cocoa_window_state
1543  *
1544  * Update a Cocoa window's state.
1545  */
1546 void macdrv_set_cocoa_window_state(macdrv_window w,
1547         const struct macdrv_window_state* state)
1548 {
1549     WineWindow* window = (WineWindow*)w;
1550
1551     OnMainThread(^{
1552         [window setMacDrvState:state];
1553     });
1554 }
1555
1556 /***********************************************************************
1557  *              macdrv_set_cocoa_window_title
1558  *
1559  * Set a Cocoa window's title.
1560  */
1561 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
1562         size_t length)
1563 {
1564     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1565     WineWindow* window = (WineWindow*)w;
1566     NSString* titleString;
1567
1568     if (title)
1569         titleString = [NSString stringWithCharacters:title length:length];
1570     else
1571         titleString = @"";
1572     OnMainThreadAsync(^{
1573         [window setTitle:titleString];
1574         if ([window isVisible] && ![window isExcludedFromWindowsMenu])
1575             [NSApp changeWindowsItem:window title:titleString filename:NO];
1576     });
1577
1578     [pool release];
1579 }
1580
1581 /***********************************************************************
1582  *              macdrv_order_cocoa_window
1583  *
1584  * Reorder a Cocoa window relative to other windows.  If prev is
1585  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
1586  * it is ordered above that window.  Otherwise, it is ordered to the
1587  * front.
1588  *
1589  * Returns true if the window has actually been ordered onto the screen
1590  * (i.e. if its frame intersects with a screen).  Otherwise, false.
1591  */
1592 int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
1593         macdrv_window next)
1594 {
1595     WineWindow* window = (WineWindow*)w;
1596     __block BOOL on_screen;
1597
1598     OnMainThread(^{
1599         on_screen = [window orderBelow:(WineWindow*)prev
1600                                orAbove:(WineWindow*)next];
1601     });
1602
1603     return on_screen;
1604 }
1605
1606 /***********************************************************************
1607  *              macdrv_hide_cocoa_window
1608  *
1609  * Hides a Cocoa window.
1610  */
1611 void macdrv_hide_cocoa_window(macdrv_window w)
1612 {
1613     WineWindow* window = (WineWindow*)w;
1614
1615     OnMainThread(^{
1616         [window doOrderOut];
1617     });
1618 }
1619
1620 /***********************************************************************
1621  *              macdrv_set_cocoa_window_frame
1622  *
1623  * Move a Cocoa window.  If the window has been moved out of the bounds
1624  * of the desktop, it is ordered out.  (This routine won't ever order a
1625  * window in, though.)
1626  *
1627  * Returns true if the window is on screen; false otherwise.
1628  */
1629 int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
1630 {
1631     WineWindow* window = (WineWindow*)w;
1632     __block BOOL on_screen;
1633
1634     OnMainThread(^{
1635         on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)];
1636     });
1637
1638     return on_screen;
1639 }
1640
1641 /***********************************************************************
1642  *              macdrv_get_cocoa_window_frame
1643  *
1644  * Gets the frame of a Cocoa window.
1645  */
1646 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
1647 {
1648     WineWindow* window = (WineWindow*)w;
1649
1650     OnMainThread(^{
1651         NSRect frame;
1652
1653         frame = [window contentRectForFrameRect:[window frame]];
1654         [[WineApplicationController sharedController] flipRect:&frame];
1655         *out_frame = NSRectToCGRect(frame);
1656     });
1657 }
1658
1659 /***********************************************************************
1660  *              macdrv_set_cocoa_parent_window
1661  *
1662  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
1663  * the parent window.
1664  */
1665 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
1666 {
1667     WineWindow* window = (WineWindow*)w;
1668
1669     OnMainThread(^{
1670         [window setMacDrvParentWindow:(WineWindow*)parent];
1671     });
1672 }
1673
1674 /***********************************************************************
1675  *              macdrv_set_window_surface
1676  */
1677 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
1678 {
1679     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1680     WineWindow* window = (WineWindow*)w;
1681
1682     OnMainThread(^{
1683         window.surface = surface;
1684         window.surface_mutex = mutex;
1685     });
1686
1687     [pool release];
1688 }
1689
1690 /***********************************************************************
1691  *              macdrv_window_needs_display
1692  *
1693  * Mark a window as needing display in a specified rect (in non-client
1694  * area coordinates).
1695  */
1696 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
1697 {
1698     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1699     WineWindow* window = (WineWindow*)w;
1700
1701     OnMainThreadAsync(^{
1702         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
1703     });
1704
1705     [pool release];
1706 }
1707
1708 /***********************************************************************
1709  *              macdrv_set_window_shape
1710  *
1711  * Sets the shape of a Cocoa window from an array of rectangles.  If
1712  * rects is NULL, resets the window's shape to its frame.
1713  */
1714 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
1715 {
1716     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1717     WineWindow* window = (WineWindow*)w;
1718
1719     OnMainThread(^{
1720         if (!rects || !count)
1721             window.shape = nil;
1722         else
1723         {
1724             NSBezierPath* path;
1725             unsigned int i;
1726
1727             path = [NSBezierPath bezierPath];
1728             for (i = 0; i < count; i++)
1729                 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
1730             window.shape = path;
1731         }
1732     });
1733
1734     [pool release];
1735 }
1736
1737 /***********************************************************************
1738  *              macdrv_set_window_alpha
1739  */
1740 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
1741 {
1742     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1743     WineWindow* window = (WineWindow*)w;
1744
1745     [window setAlphaValue:alpha];
1746
1747     [pool release];
1748 }
1749
1750 /***********************************************************************
1751  *              macdrv_set_window_color_key
1752  */
1753 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
1754                                  CGFloat keyBlue)
1755 {
1756     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1757     WineWindow* window = (WineWindow*)w;
1758
1759     OnMainThread(^{
1760         window.colorKeyed       = TRUE;
1761         window.colorKeyRed      = keyRed;
1762         window.colorKeyGreen    = keyGreen;
1763         window.colorKeyBlue     = keyBlue;
1764         [window checkTransparency];
1765     });
1766
1767     [pool release];
1768 }
1769
1770 /***********************************************************************
1771  *              macdrv_clear_window_color_key
1772  */
1773 void macdrv_clear_window_color_key(macdrv_window w)
1774 {
1775     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1776     WineWindow* window = (WineWindow*)w;
1777
1778     OnMainThread(^{
1779         window.colorKeyed = FALSE;
1780         [window checkTransparency];
1781     });
1782
1783     [pool release];
1784 }
1785
1786 /***********************************************************************
1787  *              macdrv_window_use_per_pixel_alpha
1788  */
1789 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
1790 {
1791     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1792     WineWindow* window = (WineWindow*)w;
1793
1794     OnMainThread(^{
1795         window.usePerPixelAlpha = use_per_pixel_alpha;
1796         [window checkTransparency];
1797     });
1798
1799     [pool release];
1800 }
1801
1802 /***********************************************************************
1803  *              macdrv_give_cocoa_window_focus
1804  *
1805  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
1806  * orders it front and, if its frame was not within the desktop bounds,
1807  * Cocoa will typically move it on-screen.
1808  */
1809 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
1810 {
1811     WineWindow* window = (WineWindow*)w;
1812
1813     OnMainThread(^{
1814         [window makeFocused:activate];
1815     });
1816 }
1817
1818 /***********************************************************************
1819  *              macdrv_create_view
1820  *
1821  * Creates and returns a view in the specified rect of the window.  The
1822  * caller is responsible for calling macdrv_dispose_view() on the view
1823  * when it is done with it.
1824  */
1825 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
1826 {
1827     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1828     WineWindow* window = (WineWindow*)w;
1829     __block WineContentView* view;
1830
1831     if (CGRectIsNull(rect)) rect = CGRectZero;
1832
1833     OnMainThread(^{
1834         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1835
1836         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
1837         [view setAutoresizesSubviews:NO];
1838         [nc addObserver:view
1839                selector:@selector(updateGLContexts)
1840                    name:NSViewGlobalFrameDidChangeNotification
1841                  object:view];
1842         [nc addObserver:view
1843                selector:@selector(updateGLContexts)
1844                    name:NSApplicationDidChangeScreenParametersNotification
1845                  object:NSApp];
1846         [[window contentView] addSubview:view];
1847     });
1848
1849     [pool release];
1850     return (macdrv_view)view;
1851 }
1852
1853 /***********************************************************************
1854  *              macdrv_dispose_view
1855  *
1856  * Destroys a view previously returned by macdrv_create_view.
1857  */
1858 void macdrv_dispose_view(macdrv_view v)
1859 {
1860     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1861     WineContentView* view = (WineContentView*)v;
1862
1863     OnMainThread(^{
1864         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1865
1866         [nc removeObserver:view
1867                       name:NSViewGlobalFrameDidChangeNotification
1868                     object:view];
1869         [nc removeObserver:view
1870                       name:NSApplicationDidChangeScreenParametersNotification
1871                     object:NSApp];
1872         [view removeFromSuperview];
1873         [view release];
1874     });
1875
1876     [pool release];
1877 }
1878
1879 /***********************************************************************
1880  *              macdrv_set_view_window_and_frame
1881  *
1882  * Move a view to a new window and/or position within its window.  If w
1883  * is NULL, leave the view in its current window and just change its
1884  * frame.
1885  */
1886 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
1887 {
1888     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1889     WineContentView* view = (WineContentView*)v;
1890     WineWindow* window = (WineWindow*)w;
1891
1892     if (CGRectIsNull(rect)) rect = CGRectZero;
1893
1894     OnMainThread(^{
1895         BOOL changedWindow = (window && window != [view window]);
1896         NSRect newFrame = NSRectFromCGRect(rect);
1897         NSRect oldFrame = [view frame];
1898
1899         if (changedWindow)
1900         {
1901             [view removeFromSuperview];
1902             [[window contentView] addSubview:view];
1903         }
1904
1905         if (!NSEqualRects(oldFrame, newFrame))
1906         {
1907             if (!changedWindow)
1908                 [[view superview] setNeedsDisplayInRect:oldFrame];
1909             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
1910                 [view setFrameSize:newFrame.size];
1911             else if (NSEqualSizes(oldFrame.size, newFrame.size))
1912                 [view setFrameOrigin:newFrame.origin];
1913             else
1914                 [view setFrame:newFrame];
1915             [view setNeedsDisplay:YES];
1916         }
1917     });
1918
1919     [pool release];
1920 }
1921
1922 /***********************************************************************
1923  *              macdrv_add_view_opengl_context
1924  *
1925  * Add an OpenGL context to the list being tracked for each view.
1926  */
1927 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
1928 {
1929     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1930     WineContentView* view = (WineContentView*)v;
1931     WineOpenGLContext *context = (WineOpenGLContext*)c;
1932
1933     OnMainThreadAsync(^{
1934         [view addGLContext:context];
1935     });
1936
1937     [pool release];
1938 }
1939
1940 /***********************************************************************
1941  *              macdrv_remove_view_opengl_context
1942  *
1943  * Add an OpenGL context to the list being tracked for each view.
1944  */
1945 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
1946 {
1947     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1948     WineContentView* view = (WineContentView*)v;
1949     WineOpenGLContext *context = (WineOpenGLContext*)c;
1950
1951     OnMainThreadAsync(^{
1952         [view removeGLContext:context];
1953     });
1954
1955     [pool release];
1956 }
1957
1958 /***********************************************************************
1959  *              macdrv_window_background_color
1960  *
1961  * Returns the standard Mac window background color as a 32-bit value of
1962  * the form 0x00rrggbb.
1963  */
1964 uint32_t macdrv_window_background_color(void)
1965 {
1966     static uint32_t result;
1967     static dispatch_once_t once;
1968
1969     // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
1970     // color spaces (RGB or grayscale).  So, the only way to get RGB values out
1971     // of it is to draw with it.
1972     dispatch_once(&once, ^{
1973         OnMainThread(^{
1974             unsigned char rgbx[4];
1975             unsigned char *planes = rgbx;
1976             NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
1977                                                                                pixelsWide:1
1978                                                                                pixelsHigh:1
1979                                                                             bitsPerSample:8
1980                                                                           samplesPerPixel:3
1981                                                                                  hasAlpha:NO
1982                                                                                  isPlanar:NO
1983                                                                            colorSpaceName:NSCalibratedRGBColorSpace
1984                                                                              bitmapFormat:0
1985                                                                               bytesPerRow:4
1986                                                                              bitsPerPixel:32];
1987             [NSGraphicsContext saveGraphicsState];
1988             [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
1989             [[NSColor windowBackgroundColor] set];
1990             NSRectFill(NSMakeRect(0, 0, 1, 1));
1991             [NSGraphicsContext restoreGraphicsState];
1992             [bitmap release];
1993             result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
1994         });
1995     });
1996
1997     return result;
1998 }
1999
2000 /***********************************************************************
2001  *              macdrv_send_text_input_event
2002  */
2003 int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data)
2004 {
2005     __block BOOL ret;
2006
2007     OnMainThread(^{
2008         WineWindow* window = (WineWindow*)[NSApp keyWindow];
2009         if (![window isKindOfClass:[WineWindow class]])
2010         {
2011             window = (WineWindow*)[NSApp mainWindow];
2012             if (![window isKindOfClass:[WineWindow class]] && [[NSApp orderedWineWindows] count])
2013             {
2014                 window = [[NSApp orderedWineWindows] objectAtIndex:0];
2015                 if (![window isKindOfClass:[WineWindow class]])
2016                     window = nil;
2017             }
2018         }
2019
2020         if (window)
2021         {
2022             NSUInteger localFlags = flags;
2023             CGEventRef c;
2024             NSEvent* event;
2025
2026             window.imeData = data;
2027             fix_device_modifiers_by_generic(&localFlags);
2028
2029             // An NSEvent created with +keyEventWithType:... is internally marked
2030             // as synthetic and doesn't get sent through input methods.  But one
2031             // created from a CGEvent doesn't have that problem.
2032             c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
2033             CGEventSetFlags(c, localFlags);
2034             CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
2035             event = [NSEvent eventWithCGEvent:c];
2036             CFRelease(c);
2037
2038             window.commandDone = FALSE;
2039             ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
2040         }
2041         else
2042             ret = FALSE;
2043     });
2044
2045     return ret;
2046 }