Sweden-Number/dlls/winemac.drv/cocoa_app.m

481 lines
15 KiB
Mathematica
Raw Normal View History

/*
* MACDRV Cocoa application class
*
* Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#import <Carbon/Carbon.h>
#import "cocoa_app.h"
#import "cocoa_event.h"
#import "cocoa_window.h"
int macdrv_err_on;
@interface WineApplication ()
@property (readwrite, copy, nonatomic) NSEvent* lastFlagsChanged;
@end
@implementation WineApplication
@synthesize keyboardType, lastFlagsChanged;
@synthesize orderedWineWindows;
- (id) init
{
self = [super init];
if (self != nil)
{
eventQueues = [[NSMutableArray alloc] init];
eventQueuesLock = [[NSLock alloc] init];
keyWindows = [[NSMutableArray alloc] init];
orderedWineWindows = [[NSMutableArray alloc] init];
if (!eventQueues || !eventQueuesLock || !keyWindows || !orderedWineWindows)
{
[self release];
return nil;
}
}
return self;
}
- (void) dealloc
{
[orderedWineWindows release];
[keyWindows release];
[eventQueues release];
[eventQueuesLock release];
[super dealloc];
}
- (void) transformProcessToForeground
{
if ([self activationPolicy] != NSApplicationActivationPolicyRegular)
{
NSMenu* mainMenu;
NSMenu* submenu;
NSString* bundleName;
NSString* title;
NSMenuItem* item;
[self setActivationPolicy:NSApplicationActivationPolicyRegular];
[self activateIgnoringOtherApps:YES];
mainMenu = [[[NSMenu alloc] init] autorelease];
submenu = [[[NSMenu alloc] initWithTitle:@"Wine"] autorelease];
bundleName = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString*)kCFBundleNameKey];
if ([bundleName length])
title = [NSString stringWithFormat:@"Quit %@", bundleName];
else
title = @"Quit";
item = [submenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
[item setKeyEquivalentModifierMask:NSCommandKeyMask | NSAlternateKeyMask];
item = [[[NSMenuItem alloc] init] autorelease];
[item setTitle:@"Wine"];
[item setSubmenu:submenu];
[mainMenu addItem:item];
submenu = [[[NSMenu alloc] initWithTitle:@"Window"] autorelease];
[submenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@""];
[submenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
[submenu addItem:[NSMenuItem separatorItem]];
[submenu addItemWithTitle:@"Bring All to Front" action:@selector(arrangeInFront:) keyEquivalent:@""];
item = [[[NSMenuItem alloc] init] autorelease];
[item setTitle:@"Window"];
[item setSubmenu:submenu];
[mainMenu addItem:item];
[self setMainMenu:mainMenu];
[self setWindowsMenu:submenu];
}
}
- (BOOL) registerEventQueue:(WineEventQueue*)queue
{
[eventQueuesLock lock];
[eventQueues addObject:queue];
[eventQueuesLock unlock];
return TRUE;
}
- (void) unregisterEventQueue:(WineEventQueue*)queue
{
[eventQueuesLock lock];
[eventQueues removeObjectIdenticalTo:queue];
[eventQueuesLock unlock];
}
- (void) computeEventTimeAdjustmentFromTicks:(unsigned long long)tickcount uptime:(uint64_t)uptime_ns
{
eventTimeAdjustment = (tickcount / 1000.0) - (uptime_ns / (double)NSEC_PER_SEC);
}
- (double) ticksForEventTime:(NSTimeInterval)eventTime
{
return (eventTime + eventTimeAdjustment) * 1000;
}
/* Invalidate old focus offers across all queues. */
- (void) invalidateGotFocusEvents
{
WineEventQueue* queue;
windowFocusSerial++;
[eventQueuesLock lock];
for (queue in eventQueues)
{
[queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS)
forWindow:nil];
}
[eventQueuesLock unlock];
}
- (void) windowGotFocus:(WineWindow*)window
{
macdrv_event event;
[NSApp invalidateGotFocusEvents];
event.type = WINDOW_GOT_FOCUS;
event.window = (macdrv_window)[window retain];
event.window_got_focus.serial = windowFocusSerial;
if (triedWindows)
event.window_got_focus.tried_windows = [triedWindows retain];
else
event.window_got_focus.tried_windows = [[NSMutableSet alloc] init];
[window.queue postEvent:&event];
}
- (void) windowRejectedFocusEvent:(const macdrv_event*)event
{
if (event->window_got_focus.serial == windowFocusSerial)
{
triedWindows = (NSMutableSet*)event->window_got_focus.tried_windows;
[triedWindows addObject:(WineWindow*)event->window];
for (NSWindow* window in [keyWindows arrayByAddingObjectsFromArray:[self orderedWindows]])
{
if (![triedWindows containsObject:window] && [window canBecomeKeyWindow])
{
[window makeKeyWindow];
break;
}
}
triedWindows = nil;
}
}
- (void) keyboardSelectionDidChange
{
TISInputSourceRef inputSource;
inputSource = TISCopyCurrentKeyboardLayoutInputSource();
if (inputSource)
{
CFDataRef uchr;
uchr = TISGetInputSourceProperty(inputSource,
kTISPropertyUnicodeKeyLayoutData);
if (uchr)
{
macdrv_event event;
WineEventQueue* queue;
event.type = KEYBOARD_CHANGED;
event.window = NULL;
event.keyboard_changed.keyboard_type = self.keyboardType;
event.keyboard_changed.iso_keyboard = (KBGetLayoutType(self.keyboardType) == kKeyboardISO);
event.keyboard_changed.uchr = CFDataCreateCopy(NULL, uchr);
if (event.keyboard_changed.uchr)
{
[eventQueuesLock lock];
for (queue in eventQueues)
{
CFRetain(event.keyboard_changed.uchr);
[queue postEvent:&event];
}
[eventQueuesLock unlock];
CFRelease(event.keyboard_changed.uchr);
}
}
CFRelease(inputSource);
}
}
- (CGFloat) primaryScreenHeight
{
if (!primaryScreenHeightValid)
{
NSArray* screens = [NSScreen screens];
if ([screens count])
{
primaryScreenHeight = NSHeight([[screens objectAtIndex:0] frame]);
primaryScreenHeightValid = TRUE;
}
else
return 1280; /* arbitrary value */
}
return primaryScreenHeight;
}
- (NSPoint) flippedMouseLocation:(NSPoint)point
{
/* This relies on the fact that Cocoa's mouse location points are
actually off by one (precisely because they were flipped from
Quartz screen coordinates using this same technique). */
point.y = [self primaryScreenHeight] - point.y;
return point;
}
- (void) wineWindow:(WineWindow*)window
ordered:(NSWindowOrderingMode)order
relativeTo:(WineWindow*)otherWindow
{
NSUInteger index;
switch (order)
{
case NSWindowAbove:
[window retain];
[orderedWineWindows removeObjectIdenticalTo:window];
if (otherWindow)
{
index = [orderedWineWindows indexOfObjectIdenticalTo:otherWindow];
if (index == NSNotFound)
index = 0;
}
else
{
index = 0;
for (otherWindow in orderedWineWindows)
{
if ([otherWindow level] <= [window level])
break;
index++;
}
}
[orderedWineWindows insertObject:window atIndex:index];
[window release];
break;
case NSWindowBelow:
[window retain];
[orderedWineWindows removeObjectIdenticalTo:window];
if (otherWindow)
{
index = [orderedWineWindows indexOfObjectIdenticalTo:otherWindow];
if (index == NSNotFound)
index = [orderedWineWindows count];
}
else
{
index = 0;
for (otherWindow in orderedWineWindows)
{
if ([otherWindow level] < [window level])
break;
index++;
}
}
[orderedWineWindows insertObject:window atIndex:index];
[window release];
break;
case NSWindowOut:
default:
break;
}
}
/*
* ---------- NSApplication method overrides ----------
*/
- (void) sendEvent:(NSEvent*)anEvent
{
if ([anEvent type] == NSFlagsChanged)
self.lastFlagsChanged = anEvent;
[super sendEvent:anEvent];
}
/*
* ---------- NSApplicationDelegate methods ----------
*/
- (void)applicationDidChangeScreenParameters:(NSNotification *)notification
{
primaryScreenHeightValid = FALSE;
}
- (void)applicationDidResignActive:(NSNotification *)notification
{
macdrv_event event;
WineEventQueue* queue;
[self invalidateGotFocusEvents];
event.type = APP_DEACTIVATED;
event.window = NULL;
[eventQueuesLock lock];
for (queue in eventQueues)
[queue postEvent:&event];
[eventQueuesLock unlock];
}
- (void)applicationWillFinishLaunching:(NSNotification *)notification
{
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc addObserverForName:NSWindowDidBecomeKeyNotification
object:nil
queue:nil
usingBlock:^(NSNotification *note){
NSWindow* window = [note object];
[keyWindows removeObjectIdenticalTo:window];
[keyWindows insertObject:window atIndex:0];
}];
[nc addObserverForName:NSWindowWillCloseNotification
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note){
NSWindow* window = [note object];
[keyWindows removeObjectIdenticalTo:window];
[orderedWineWindows removeObjectIdenticalTo:window];
}];
[nc addObserver:self
selector:@selector(keyboardSelectionDidChange)
name:NSTextInputContextKeyboardSelectionDidChangeNotification
object:nil];
/* The above notification isn't sent unless the NSTextInputContext
class has initialized itself. Poke it. */
[NSTextInputContext self];
self.keyboardType = LMGetKbdType();
}
@end
/***********************************************************************
* OnMainThread
*
* Run a block on the main thread synchronously.
*/
void OnMainThread(dispatch_block_t block)
{
dispatch_sync(dispatch_get_main_queue(), block);
}
/***********************************************************************
* OnMainThreadAsync
*
* Run a block on the main thread asynchronously.
*/
void OnMainThreadAsync(dispatch_block_t block)
{
dispatch_async(dispatch_get_main_queue(), block);
}
/***********************************************************************
* LogError
*/
void LogError(const char* func, NSString* format, ...)
{
va_list args;
va_start(args, format);
LogErrorv(func, format, args);
va_end(args);
}
/***********************************************************************
* LogErrorv
*/
void LogErrorv(const char* func, NSString* format, va_list args)
{
NSString* message = [[NSString alloc] initWithFormat:format arguments:args];
fprintf(stderr, "err:%s:%s", func, [message UTF8String]);
[message release];
}
/***********************************************************************
* macdrv_window_rejected_focus
*
* Pass focus to the next window that hasn't already rejected this same
* WINDOW_GOT_FOCUS event.
*/
void macdrv_window_rejected_focus(const macdrv_event *event)
{
OnMainThread(^{
[NSApp windowRejectedFocusEvent:event];
});
}
/***********************************************************************
* macdrv_get_keyboard_layout
*
* Returns the keyboard layout uchr data.
*/
CFDataRef macdrv_copy_keyboard_layout(CGEventSourceKeyboardType* keyboard_type, int* is_iso)
{
__block CFDataRef result = NULL;
OnMainThread(^{
TISInputSourceRef inputSource;
inputSource = TISCopyCurrentKeyboardLayoutInputSource();
if (inputSource)
{
CFDataRef uchr = TISGetInputSourceProperty(inputSource,
kTISPropertyUnicodeKeyLayoutData);
result = CFDataCreateCopy(NULL, uchr);
CFRelease(inputSource);
*keyboard_type = ((WineApplication*)NSApp).keyboardType;
*is_iso = (KBGetLayoutType(*keyboard_type) == kKeyboardISO);
}
});
return result;
}
2013-02-06 11:36:49 +01:00
/***********************************************************************
* macdrv_beep
*
* Play the beep sound configured by the user in System Preferences.
*/
void macdrv_beep(void)
{
OnMainThreadAsync(^{
NSBeep();
});
}