diff --git a/.travis.yml b/.travis.yml index ecb798863..a0cbca94f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,6 @@ before_install: export PATH="/usr/local/opt/gettext/bin:$PATH"; export PKG_CONFIG_PATH=/usr/local/opt/libffi/lib/pkgconfig/:$PKG_CONFIG_PATH; ls /usr/local/lib/gstreamer-1.0; - ls /usr/local/lib/gio/modules; fi before_script: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build cmake -Hstrawberry -Bbuild ; fi diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5a3bff843..40b546077 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -837,6 +837,7 @@ endif() # Platform specific - macOS optional_source(APPLE SOURCES + core/mac_utilities.mm core/mac_startup.mm core/macsystemtrayicon.mm core/macscreensaver.cpp diff --git a/src/core/mac_startup.mm b/src/core/mac_startup.mm index 8cd3c3e6c..eedfa6e09 100644 --- a/src/core/mac_startup.mm +++ b/src/core/mac_startup.mm @@ -68,19 +68,23 @@ #include QDebug operator<<(QDebug dbg, NSObject* object) { - QString ns_format = [[NSString stringWithFormat:@"%@", object] UTF8String]; + + QString ns_format = [ [NSString stringWithFormat:@"%@", object] UTF8String]; dbg.nospace() << ns_format; return dbg.space(); + } // Capture global media keys on Mac (Cocoa only!) // See: http://www.rogueamoeba.com/utm/2007/09/29/apple-keyboard-media-key-event-handling/ @interface MacApplication : NSApplication { + PlatformInterface* application_handler_; AppDelegate* delegate_; // shortcut_handler_ only used to temporarily save it AppDelegate does all the heavy-shortcut-lifting GlobalShortcutBackendMacOS* shortcut_handler_; + } - (GlobalShortcutBackendMacOS*)shortcut_handler; @@ -91,52 +95,36 @@ QDebug operator<<(QDebug dbg, NSObject* object) { @end -#ifdef HAVE_BREAKPAD -static bool BreakpadCallback(int, int, mach_port_t, void*) { return true; } - -static BreakpadRef InitBreakpad() { - ScopedNSAutoreleasePool pool; - BreakpadRef breakpad = nil; - NSDictionary* plist = [[NSBundle mainBundle] infoDictionary]; - if (plist) { - breakpad = BreakpadCreate(plist); - BreakpadSetFilterCallback(breakpad, &BreakpadCallback, nullptr); - } - [pool release]; - return breakpad; -} -#endif // HAVE_BREAKPAD - @implementation AppDelegate - (id)init { + if ((self = [super init])) { application_handler_ = nil; shortcut_handler_ = nil; dock_menu_ = nil; } return self; + } - (id)initWithHandler:(PlatformInterface*)handler { + application_handler_ = handler; -#ifdef HAVE_BREAKPAD - breakpad_ = InitBreakpad(); -#endif - // Register defaults for the whitelist of apps that want to use media keys - [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys: - [SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers], kMediaKeyUsingBundleIdentifiersDefaultsKey, - nil]]; + [ [NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:[SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers], kMediaKeyUsingBundleIdentifiersDefaultsKey, nil] ]; return self; + } - (BOOL) applicationShouldHandleReopen: (NSApplication*)app hasVisibleWindows:(BOOL)flag { + if (application_handler_) { application_handler_->Activate(); } return YES; + } - (void)setDockMenu:(NSMenu*)menu { @@ -156,21 +144,19 @@ static BreakpadRef InitBreakpad() { } - (void)applicationDidFinishLaunching:(NSNotification*)aNotification { - key_tap_ = [[SPMediaKeyTap alloc] initWithDelegate:self]; - if ([SPMediaKeyTap usesGlobalMediaKeyTap] && - ![[NSProcessInfo processInfo] - isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){ - .majorVersion = 10, - .minorVersion = 12, - .patchVersion = 0}]) { + + key_tap_ = [ [SPMediaKeyTap alloc] initWithDelegate:self]; + if ([SPMediaKeyTap usesGlobalMediaKeyTap] && ![ [NSProcessInfo processInfo]isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){.majorVersion = 10, .minorVersion = 12, .patchVersion = 0}]) { [key_tap_ startWatchingMediaKeys]; } else { qLog(Warning) << "Media key monitoring disabled"; } + } - (BOOL)application:(NSApplication*)app openFile:(NSString*)filename { + qLog(Debug) << "Wants to open:" << [filename UTF8String]; if (application_handler_->LoadUrl(QString::fromUtf8([filename UTF8String]))) { @@ -178,16 +164,20 @@ static BreakpadRef InitBreakpad() { } return NO; + } - (void)application:(NSApplication*)app openFiles:(NSArray*)filenames { + qLog(Debug) << "Wants to open:" << filenames; [filenames enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL* stop) { - [self application:app openFile:(NSString*)object]; - }]; + [self application:app openFile:(NSString*)object]; + }]; + } - (void) mediaKeyTap: (SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event { + NSAssert([event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys, @"Unexpected NSEvent in mediaKeyTap:receivedMediaKeyEvent:"); int key_code = (([event data1] & 0xFFFF0000) >> 16); @@ -202,12 +192,10 @@ static BreakpadRef InitBreakpad() { if (key_is_released) { shortcut_handler_->MacMediaKeyPressed(key_code); } + } - (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication*) sender { -#ifdef HAVE_BREAKPAD - BreakpadRelease(breakpad_); -#endif return NSTerminateNow; } @@ -242,17 +230,19 @@ static BreakpadRef InitBreakpad() { } - (void)SetApplicationHandler:(PlatformInterface*)handler { - delegate_ = [[AppDelegate alloc] initWithHandler:handler]; + + delegate_ = [ [AppDelegate alloc] initWithHandler:handler]; // App-shortcut-handler set before delegate is set. // this makes sure the delegate's shortcut_handler is set [delegate_ setShortcutHandler:shortcut_handler_]; [self setDelegate:delegate_]; - [[NSUserNotificationCenter defaultUserNotificationCenter] - setDelegate:delegate_]; + [ [NSUserNotificationCenter defaultUserNotificationCenter]setDelegate:delegate_]; + } - (void)sendEvent:(NSEvent*)event { + // If event tap is not installed, handle events that reach the app instead BOOL shouldHandleMediaKeyEventLocally = ![SPMediaKeyTap usesGlobalMediaKeyTap]; @@ -261,6 +251,7 @@ static BreakpadRef InitBreakpad() { } [super sendEvent:event]; + } @end @@ -268,13 +259,15 @@ static BreakpadRef InitBreakpad() { namespace mac { void MacMain() { + ScopedNSAutoreleasePool pool; // Creates and sets the magic global variable so QApplication will find it. [MacApplication sharedApplication]; #ifdef HAVE_SPARKLE // Creates and sets the magic global variable for Sparkle. - [[SUUpdater sharedUpdater] setDelegate:NSApp]; + [ [SUUpdater sharedUpdater] setDelegate:NSApp]; #endif + } void SetShortcutHandler(GlobalShortcutBackendMacOS* handler) { @@ -287,52 +280,61 @@ void SetApplicationHandler(PlatformInterface* handler) { void CheckForUpdates() { #ifdef HAVE_SPARKLE - [[SUUpdater sharedUpdater] checkForUpdates:NSApp]; + [ [SUUpdater sharedUpdater] checkForUpdates:NSApp]; #endif } QString GetBundlePath() { + ScopedCFTypeRef app_url(CFBundleCopyBundleURL(CFBundleGetMainBundle())); ScopedCFTypeRef mac_path(CFURLCopyFileSystemPath(app_url.get(), kCFURLPOSIXPathStyle)); const char* path = CFStringGetCStringPtr(mac_path.get(), CFStringGetSystemEncoding()); QString bundle_path = QString::fromUtf8(path); return bundle_path; + } QString GetResourcesPath() { + QString bundle_path = GetBundlePath(); return bundle_path + "/Contents/Resources"; + } QString GetApplicationSupportPath() { + ScopedNSAutoreleasePool pool; - NSArray* paths = NSSearchPathForDirectoriesInDomains( - NSApplicationSupportDirectory, NSUserDomainMask, YES); + NSArray* paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); QString ret; if ([paths count] > 0) { NSString* user_path = [paths objectAtIndex:0]; ret = QString::fromUtf8([user_path UTF8String]); - } else { + } + else { ret = "~/Library/Application Support"; } return ret; + } QString GetMusicDirectory() { + ScopedNSAutoreleasePool pool; - NSArray* paths = NSSearchPathForDirectoriesInDomains(NSMusicDirectory, - NSUserDomainMask, YES); + NSArray* paths = NSSearchPathForDirectoriesInDomains(NSMusicDirectory, NSUserDomainMask, YES); QString ret; if ([paths count] > 0) { NSString* user_path = [paths objectAtIndex:0]; ret = QString::fromUtf8([user_path UTF8String]); - } else { + } + else { ret = "~/Music"; } return ret; + } static int MapFunctionKey(int keycode) { + switch (keycode) { // Function keys case NSInsertFunctionKey: return Qt::Key_Insert; @@ -392,6 +394,7 @@ static int MapFunctionKey(int keycode) { } QKeySequence KeySequenceFromNSEvent(NSEvent* event) { + NSString* str = [event charactersIgnoringModifiers]; NSString* upper = [str uppercaseString]; const char* chars = [upper UTF8String]; @@ -409,7 +412,8 @@ QKeySequence KeySequenceFromNSEvent(NSEvent* event) { if (key == 0) { if (c >= 0x20 && c <= 0x7e) { // ASCII from space to ~ key = c; - } else { + } + else { key = MapFunctionKey([event keyCode]); if (key == 0) { return QKeySequence(); @@ -431,11 +435,14 @@ QKeySequence KeySequenceFromNSEvent(NSEvent* event) { } return QKeySequence(key); + } void DumpDictionary(CFDictionaryRef dict) { + NSDictionary* d = (NSDictionary*)dict; NSLog(@"%@", d); + } // NSWindowCollectionBehaviorFullScreenPrimary @@ -446,11 +453,14 @@ void EnableFullScreen(const QWidget& main_window) { NSView* view = reinterpret_cast(main_window.winId()); NSWindow* window = [view window]; [window setCollectionBehavior:kFullScreenPrimary]; + } float GetDevicePixelRatio(QWidget* widget) { + NSView* view = reinterpret_cast(widget->winId()); - return [[view window] backingScaleFactor]; + return [ [view window] backingScaleFactor]; + } } // namespace mac diff --git a/src/core/mac_utilities.h b/src/core/mac_utilities.h index a96f6df79..5527394a2 100644 --- a/src/core/mac_utilities.h +++ b/src/core/mac_utilities.h @@ -37,3 +37,7 @@ QKeySequence KeySequenceFromNSEvent(NSEvent* event); void DumpDictionary(CFDictionaryRef dict); float GetDevicePixelRatio(QWidget *widget); } + +namespace Utilities { +qint32 GetMacOsVersion(); +} diff --git a/src/core/mac_utilities.mm b/src/core/mac_utilities.mm new file mode 100644 index 000000000..eb6d5d8a1 --- /dev/null +++ b/src/core/mac_utilities.mm @@ -0,0 +1,36 @@ +/* + * Strawberry Music Player + * Copyright 2019, Jonas Kvinge + * + * Strawberry is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Strawberry 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Strawberry. If not, see . + * + */ + +#include "config.h" + +#include + +#import +#import + +namespace Utilities { + +qint32 GetMacOsVersion() { + + NSOperatingSystemVersion version = [ [NSProcessInfo processInfo] operatingSystemVersion]; + return version.minorVersion; + +} + +} // namespace Utilities diff --git a/src/core/macscreensaver.cpp b/src/core/macscreensaver.cpp index 9c12a211c..77eef8c62 100644 --- a/src/core/macscreensaver.cpp +++ b/src/core/macscreensaver.cpp @@ -25,6 +25,7 @@ #include #include "core/utilities.h" +#include "core/mac_utilities.h" // kIOPMAssertionTypePreventUserIdleDisplaySleep from Lion. #define kLionDisplayAssertion CFSTR("PreventUserIdleDisplaySleep") diff --git a/src/core/utilities.cpp b/src/core/utilities.cpp index 710591266..bad01c895 100644 --- a/src/core/utilities.cpp +++ b/src/core/utilities.cpp @@ -329,14 +329,6 @@ QString ColorToRgba(const QColor &c) { } #ifdef Q_OS_MACOS -qint32 GetMacOsVersion() { - - SInt32 minor_version; - Gestalt(gestaltSystemVersionMinor, &minor_version); - return minor_version; - -} - // Better than openUrl(dirname(path)) - also highlights file at path void RevealFileInFinder(QString const &path) { QProcess::execute("/usr/bin/open", QStringList() << "-R" << path); diff --git a/src/core/utilities.h b/src/core/utilities.h index 428195ca5..5299b7485 100644 --- a/src/core/utilities.h +++ b/src/core/utilities.h @@ -125,9 +125,6 @@ void SetEnv(const char *key, const QString &value); void IncreaseFDLimit(); void CheckPortable(); -// Returns the minor version of OS X (ie. 6 for Snow Leopard, 7 for Lion). -qint32 GetMacOsVersion(); - // Borrowed from schedutils enum IoPriority { IOPRIO_CLASS_NONE = 0, diff --git a/src/globalshortcuts/globalshortcutbackend-macos.mm b/src/globalshortcuts/globalshortcutbackend-macos.mm index 6176c5b80..8ea66a1d2 100644 --- a/src/globalshortcuts/globalshortcutbackend-macos.mm +++ b/src/globalshortcuts/globalshortcutbackend-macos.mm @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -50,15 +51,13 @@ class GlobalShortcutBackendMacOSPrivate : boost::noncopyable { : global_monitor_(nil), local_monitor_(nil), backend_(backend) {} bool Register() { - global_monitor_ = [NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask - handler:^(NSEvent* event) { - HandleKeyEvent(event); - }]; - local_monitor_ = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask - handler:^(NSEvent* event) { + global_monitor_ = [NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask handler:^(NSEvent* event) { + HandleKeyEvent(event); + } ]; + local_monitor_ = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask handler:^(NSEvent* event) { // Filter event if we handle it as a global shortcut. return HandleKeyEvent(event) ? nil : event; - }]; + } ]; return true; } @@ -85,6 +84,7 @@ GlobalShortcutBackendMacOS::GlobalShortcutBackendMacOS(GlobalShortcuts* parent) GlobalShortcutBackendMacOS::~GlobalShortcutBackendMacOS() {} bool GlobalShortcutBackendMacOS::DoRegister() { + // Always enable media keys. mac::SetShortcutHandler(this); @@ -97,11 +97,14 @@ bool GlobalShortcutBackendMacOS::DoRegister() { } void GlobalShortcutBackendMacOS::DoUnregister() { + p_->Unregister(); shortcuts_.clear(); + } void GlobalShortcutBackendMacOS::MacMediaKeyPressed(int key) { + switch (key) { case NX_KEYTYPE_PLAY: KeyPressed(Qt::Key_MediaPlay); @@ -113,9 +116,11 @@ void GlobalShortcutBackendMacOS::MacMediaKeyPressed(int key) { KeyPressed(Qt::Key_MediaPrevious); break; } + } bool GlobalShortcutBackendMacOS::KeyPressed(const QKeySequence& sequence) { + if (sequence.isEmpty()) { return false; } @@ -125,24 +130,27 @@ bool GlobalShortcutBackendMacOS::KeyPressed(const QKeySequence& sequence) { return true; } return false; + } bool GlobalShortcutBackendMacOS::IsAccessibilityEnabled() const { - return AXAPIEnabled(); + + NSDictionary *options = @{(id)kAXTrustedCheckOptionPrompt: @YES}; + return AXIsProcessTrustedWithOptions((CFDictionaryRef)options); + } void GlobalShortcutBackendMacOS::ShowAccessibilityDialog() { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSPreferencePanesDirectory, NSSystemDomainMask, YES); if ([paths count] == 1) { - SBSystemPreferencesApplication* system_prefs = [SBApplication - applicationWithBundleIdentifier:@"com.apple.systempreferences"]; + SBSystemPreferencesApplication* system_prefs = [SBApplication applicationWithBundleIdentifier:@"com.apple.systempreferences"]; [system_prefs activate]; SBElementArray* panes = [system_prefs panes]; SBSystemPreferencesPane* security_pane = nil; for (SBSystemPreferencesPane* pane : panes) { - if ([[pane id] isEqualToString:@"com.apple.preference.security"]) { + if ([ [pane id] isEqualToString:@"com.apple.preference.security"]) { security_pane = pane; break; } @@ -151,7 +159,7 @@ void GlobalShortcutBackendMacOS::ShowAccessibilityDialog() { SBElementArray* anchors = [security_pane anchors]; for (SBSystemPreferencesAnchor* anchor : anchors) { - if ([[anchor name] isEqualToString:@"Privacy_Accessibility"]) { + if ([ [anchor name] isEqualToString:@"Privacy_Accessibility"]) { [anchor reveal]; } } diff --git a/src/settings/shortcutssettingspage.cpp b/src/settings/shortcutssettingspage.cpp index 5a71d27cc..2a4c342ce 100644 --- a/src/settings/shortcutssettingspage.cpp +++ b/src/settings/shortcutssettingspage.cpp @@ -40,6 +40,9 @@ #include "core/iconloader.h" #include "core/utilities.h" +#ifdef Q_OS_MACOS +# include "core/mac_utilities.h" +#endif #include "globalshortcuts/globalshortcutgrabber.h" #include "globalshortcuts/globalshortcuts.h" #include "settingspage.h" diff --git a/src/widgets/qocoa_mac.h b/src/widgets/qocoa_mac.h index 2456d6612..1a58e3523 100644 --- a/src/widgets/qocoa_mac.h +++ b/src/widgets/qocoa_mac.h @@ -27,30 +27,33 @@ THE SOFTWARE. #include #include -static inline NSString* fromQString(const QString &string) -{ - const QByteArray utf8 = string.toUtf8(); - const char* cString = utf8.constData(); - return [[NSString alloc] initWithUTF8String:cString]; +static inline NSString* fromQString(const QString &string) { + + const QByteArray utf8 = string.toUtf8(); + const char* cString = utf8.constData(); + return [ [NSString alloc] initWithUTF8String:cString]; + } -static inline QString toQString(NSString *string) -{ - if (!string) - return QString(); - return QString::fromUtf8([string UTF8String]); +static inline QString toQString(NSString *string) { + + if (!string) return QString(); + return QString::fromUtf8([string UTF8String]); + } -static inline NSImage* fromQPixmap(const QPixmap &pixmap) -{ - CGImageRef cgImage = QtMac::toCGImageRef(pixmap); - return [[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize]; +static inline NSImage* fromQPixmap(const QPixmap &pixmap) { + + CGImageRef cgImage = QtMac::toCGImageRef(pixmap); + return [ [NSImage alloc] initWithCGImage:cgImage size:NSZeroSize]; + } -static inline void setupLayout(NSView *cocoaView, QWidget *parent) -{ - parent->setAttribute(Qt::WA_NativeWindow); - QVBoxLayout *layout = new QVBoxLayout(parent); - layout->setMargin(0); - layout->addWidget(new QMacCocoaViewContainer(cocoaView, parent)); +static inline void setupLayout(NSView *cocoaView, QWidget *parent) { + + parent->setAttribute(Qt::WA_NativeWindow); + QVBoxLayout *layout = new QVBoxLayout(parent); + layout->setMargin(0); + layout->addWidget(new QMacCocoaViewContainer(cocoaView, parent)); + }