// This file is part of BOINC. // http://boinc.berkeley.edu // Copyright (C) 2016 University of California // // BOINC 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 3 of the License, or (at your option) any later version. // // BOINC 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 BOINC. If not, see . // BOINCGUIApp.mm #include "MacGUI.pch" #include "BOINCGUIApp.h" #include "BOINCBaseFrame.h" #import #if !wxCHECK_VERSION(3,0,1) // This should be fixed after wxCocoa 3.0.0: // http://trac.wxwidgets.org/ticket/16156 #ifndef NSEventTypeApplicationDefined #define NSEventTypeApplicationDefined NSApplicationDefined #endif // Cocoa routines which are part of CBOINCGUIApp // Override standard wxCocoa wxApp::CallOnInit() to allow Manager // to run properly when launched hidden on login via Login Item. bool CBOINCGUIApp::CallOnInit() { NSAutoreleasePool *mypool = [[NSAutoreleasePool alloc] init]; NSEvent *event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined location:NSMakePoint(0.0, 0.0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil subtype:0 data1:0 data2:0]; [NSApp postEvent:event atStart:FALSE]; bool retVal = wxApp::CallOnInit(); [mypool release]; return retVal; } #endif // Our application can get into a strange state // if our login item launched it hidden and the // first time the user "opens" it he either // double-clicks on our Finder icon or uses // command-tab. It becomes the frontmost // application (with its menu in the menubar) // but the windows remain hidden, and it does // not receive an activate event, so we must // handle this case by polling. // // We can stop the polling after the windows // have been shown once, since this state only // occurs if the windows have never appeared. // // TODO: Can we avoid polling by using notification // TODO: [NSApplicationDelegate applicationDidUnhide: ] ? // void CBOINCGUIApp::CheckPartialActivation() { // This code is not needed and has bad effects on OS 10.5. // Initializing wasHidden this way avoids the problem // because we are briefly shown at login on OS 10.5. static bool wasHidden = [ NSApp isHidden ]; if (wasHidden) { if (m_bAboutDialogIsOpen) return; if (! [ NSApp isHidden ]) { wasHidden = false; ShowInterface(); } } } // Returns true if file was modified since system was booted, else false // bool CBOINCGUIApp::WasFileModifiedBeforeSystemBoot(char * filePath) { NSTimeInterval upTime = [[NSProcessInfo processInfo] systemUptime]; NSString *path = [NSString stringWithUTF8String:filePath]; NSError *error = nil; NSDictionary * attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&error]; if (attrs && !error) { // If file exists, then ... NSDate *fileLastModifiedDate = [attrs fileModificationDate]; NSTimeInterval ageOfFile = -[fileLastModifiedDate timeIntervalSinceNow]; return (ageOfFile > upTime); } return false; } // HideThisApp() is called from CBOINCGUIApp::ShowApplication(bool) // and replaces a call of ShowHideProcess() which is deprecated // under OS 10.9. void CBOINCGUIApp::HideThisApp() { [ NSApp hide:NSApp ]; } /// Determines if the current process is visible. /// /// @return /// true if the current process is visible, otherwise false. /// bool CBOINCGUIApp::IsApplicationVisible() { return (! [ NSApp isHidden ]); } /// /// Shows or hides the current process. /// /// @param bShow /// true will show the process, false will hide the process. /// void CBOINCGUIApp::ShowApplication(bool bShow) { if (bShow) { [ NSApp activateIgnoringOtherApps:YES ]; } else { [ NSApp hide:NSApp ]; } } /// /// Gets the display name for this app void CBOINCGUIApp::getDisplayNameForThisApp(char* pathBuf, size_t bufSize) { // Get the app's main bundle NSBundle *main = [NSBundle mainBundle]; NSString *thePath = main.localizedInfoDictionary[(NSString *)kCFBundleNameKey]; if (thePath == nil) { thePath = [NSProcessInfo processInfo].processName; } strlcpy(pathBuf, [thePath UTF8String], bufSize); } // NSTitledWindowMask is deprecated in OS 10.12 and is replaced by // NSWindowStyleMaskTitled, which is not defined before OS 10.12 #ifndef NSWindowStyleMaskTitled #define NSWindowStyleMaskTitled NSTitledWindowMask #endif // Returns true if at least a 5 X 5 pixel area of the // window's title bar is entirely on the displays // Note: Arguments are wxWidgets / Quickdraw-style coordinates // (y=0 at top) but NSScreen coordinates have y=0 at bottom Boolean IsWindowOnScreen(int iLeft, int iTop, int iWidth, int iHeight) { NSRect testFrameRect = NSMakeRect(100, 100, 200, 200); NSRect testContentRect = [NSWindow contentRectForFrameRect:testFrameRect styleMask:NSWindowStyleMaskTitled]; CGFloat titleBarHeight = testFrameRect.size.height - testContentRect.size.height; // Make sure at least a 5X5 piece of title bar is visible NSRect titleRect = NSMakeRect(iLeft, iTop, iWidth, titleBarHeight); titleRect = NSInsetRect(titleRect, 5, 5); NSArray *allScreens = [NSScreen screens]; unsigned int i; // The geometries of windows and display arangements are such // that even if the title bar spans multiple windows, a 5X5 // section is on-screen only if at least one 5X5 section is // entirely on one or more displays, so this test is sufficient. unsigned int numDisplays = [ allScreens count ]; for (i=0; iGetEventHandler()->AddPendingEvent(evt); return noErr; }