11// This file is part of BOINC.
22// http://boinc.berkeley.edu
3- // Copyright (C) 2024 University of California
3+ // Copyright (C) 2025 University of California
44//
55// BOINC is free software; you can redistribute it and/or modify it
66// under the terms of the GNU Lesser General Public License
@@ -149,6 +149,7 @@ bool CBOINCGUIApp::OnInit() {
149149 m_iDisplayAnotherInstanceRunningDialog = 1 ;
150150#ifdef __WXMAC__
151151 m_iHideMenuBarIcon = 0 ;
152+ m_iWasShutDownBySystemWhileHidden = 0 ;
152153#endif
153154 m_iGUISelected = BOINC_SIMPLEGUI;
154155 m_bSafeMessageBoxDisplayed = 0 ;
@@ -232,6 +233,18 @@ bool CBOINCGUIApp::OnInit() {
232233 m_pConfig->Read (wxT (" DisplayAnotherInstanceRunningDialog" ), &m_iDisplayAnotherInstanceRunningDialog, 1L );
233234#ifdef __WXMAC__
234235 m_pConfig->Read (wxT (" HideMenuBarIcon" ), &m_iHideMenuBarIcon, 0L );
236+ m_pConfig->Read (wxT (" WasShutDownBySystemWhileHidden" ), &m_iWasShutDownBySystemWhileHidden, 0L );
237+ // If Manager was hidden and was shut down by system when user last logged
238+ // out, MacOS's "Reopen windows when logging in" functionality may relaunch
239+ // us visible before our LaunchAgent launches us with the "autostart" arg.
240+ // QuitAppleEventHandler() set m_iWasShutDownBySystemWhileHidden to 1, causing
241+ // CBOINCGUIApp::SaveState to set WasShutDownBySystemWhileHidden in our
242+ // configuration file to tell us to treat this as an autostart and launch hidden.
243+ if (m_iWasShutDownBySystemWhileHidden) {
244+ m_iWasShutDownBySystemWhileHidden = 0 ;
245+ m_bBOINCMGRAutoStarted = true ;
246+ m_bGUIVisible = false ;
247+ }
235248#endif
236249 m_pConfig->Read (wxT (" DisableAutoStart" ), &m_iBOINCMGRDisableAutoStart, 0L );
237250 m_pConfig->Read (wxT (" LanguageISO" ), &m_strISOLanguageCode, wxT (" " ));
@@ -395,7 +408,6 @@ bool CBOINCGUIApp::OnInit() {
395408 g_use_sandbox, true , path_to_error, sizeof (path_to_error)
396409 );
397410 }
398-
399411 if (iErrorCode) {
400412
401413#if (defined(__WXMAC__) && (!defined (_DEBUG)))
@@ -490,6 +502,16 @@ bool CBOINCGUIApp::OnInit() {
490502 // Detect if BOINC Manager is already running, if so, bring it into the
491503 // foreground and then exit.
492504 if (DetectDuplicateInstance ()) {
505+ #ifdef __WXMAC__
506+ SetActivationPolicyAccessory (true );
507+ // Hack to work around an issue with the Mac Installer
508+ if (m_bBOINCMGRAutoStarted) return false ;
509+ // TODO: Should we also ignore duplicate instances that occur within
510+ // TODO: a short time after login (detemined by getlastlogxbyname(),
511+ // TODO: in case MacOS's "Reopen windows when logging in" functionality
512+ // TODO: relaunched us after our LaunchAgent autostarted us?
513+
514+ #endif
493515 if (GetBOINCMGRDisplayAnotherInstanceRunningMessage ()) {
494516 wxString appName = m_pSkinManager->GetAdvanced ()->GetApplicationName ();
495517 wxString message;
@@ -501,11 +523,19 @@ bool CBOINCGUIApp::OnInit() {
501523 CDlgGenericMessage dlg (NULL , ¶ms);
502524 dlg.ShowModal ();
503525 SetBOINCMGRDisplayAnotherInstanceRunningMessage (!dlg.GetDisableMessageValue ());
504- SaveState ();
505526 }
506527 return false ;
507528 }
508-
529+ #ifdef __WXMAC__
530+ // Each time a second instance of BOINC Managr is launched, it normally
531+ // nadds an additional BOINC icon to the "recently run apps" section
532+ // of the Dock, cluttering it up. To avoid this, we set the LSUIElement
533+ // key in its info.plist, which prevents the app from appearing in the
534+ // Dock. But if this is not a duplicate instance, we call this routine
535+ // to tell the system to show the icon in the Dock.
536+ // https://stackoverflow.com/questions/620841/how-to-hide-the-dock-icon
537+ SetActivationPolicyAccessory (false ); // Show our Dock tile
538+ #endif
509539 // Initialize the main document
510540 m_pDocument = new CMainDocument ();
511541 wxASSERT (m_pDocument);
@@ -686,6 +716,7 @@ void CBOINCGUIApp::SaveState() {
686716 m_pConfig->Write (wxT (" DisplayAnotherInstanceRunningDialog" ), m_iDisplayAnotherInstanceRunningDialog);
687717#ifdef __WXMAC__
688718 m_pConfig->Write (wxT (" HideMenuBarIcon" ), m_iHideMenuBarIcon);
719+ m_pConfig->Write (wxT (" WasShutDownBySystemWhileHidden" ), m_iWasShutDownBySystemWhileHidden);
689720#endif
690721 m_pConfig->Write (wxT (" DisableAutoStart" ), m_iBOINCMGRDisableAutoStart);
691722 m_pConfig->Write (wxT (" RunDaemon" ), m_bRunDaemon);
@@ -1105,30 +1136,41 @@ int CBOINCGUIApp::IdleTrackerDetach() {
11051136void CBOINCGUIApp::OnActivateApp (wxActivateEvent& event) {
11061137 m_bProcessingActivateAppEvent = true ;
11071138
1108-
11091139#ifndef __WXMSW__ // On Win, the following raises the wrong window
11101140 if (event.GetActive ())
11111141#endif
11121142 {
11131143#ifdef __WXMAC__
1114- ShowInterface ();
1115- #elif defined(__WXGTK__)
1116- // Linux allows the Event Log to be brought forward and made active
1117- // even if we have a modal dialog displayed (associated with our
1118- // main frame.) This test is needed to allow bringing the modal
1119- // dialog forward again by clicking on its title bar.
1120- if (!IsModalDialogDisplayed ())
1144+ // When our LaunchAgent / login item launches us at login, it activates
1145+ // this app, but we want it hidden. So we immediatly hide it upon the
1146+ // first activation when run as a login item.
1147+ static bool first = true ;
1148+
1149+ CMainDocument* pDoc = wxGetApp ().GetDocument ();
1150+ if (m_bBOINCMGRAutoStarted && (!pDoc->IsConnected ()) && first) {
1151+ first = false ;
1152+ ShowApplication (false );
1153+ } else
1154+ #endif
1155+ #if (defined (__WXMAC__) || defined(__WXGTK__))
11211156 {
1122- bool keepEventLogInFront = m_bEventLogWasActive;
1123-
1124- if (m_pEventLog && !m_pEventLog->IsIconized () && !keepEventLogInFront) {
1125- m_pEventLog->Raise ();
1126- }
1127- if (m_pFrame) {
1128- m_pFrame->Raise ();
1129- }
1130- if (m_pEventLog && !m_pEventLog->IsIconized () && keepEventLogInFront) {
1131- m_pEventLog->Raise ();
1157+ // Linux allows the Event Log to be brought forward and made active
1158+ // even if we have a modal dialog displayed (associated with our
1159+ // main frame.) This test is needed to allow bringing the modal
1160+ // dialog forward again by clicking on its title bar.
1161+ if (!IsModalDialogDisplayed ())
1162+ {
1163+ bool keepEventLogInFront = m_bEventLogWasActive;
1164+
1165+ if (m_pEventLog && !m_pEventLog->IsIconized () && !keepEventLogInFront) {
1166+ m_pEventLog->Raise ();
1167+ }
1168+ if (m_pFrame) {
1169+ m_pFrame->Raise ();
1170+ }
1171+ if (m_pEventLog && !m_pEventLog->IsIconized () && keepEventLogInFront) {
1172+ m_pEventLog->Raise ();
1173+ }
11321174 }
11331175 }
11341176#endif
@@ -1536,6 +1578,7 @@ bool CBOINCGUIApp::IsModalDialogDisplayed() {
15361578 return false ;
15371579}
15381580
1581+
15391582// Prevent recursive entry of CMainDocument::RequestRPC()
15401583int CBOINCGUIApp::FilterEvent (wxEvent &event) {
15411584 int theEventType;
@@ -1588,4 +1631,3 @@ int CBOINCGUIApp::FilterEvent(wxEvent &event) {
15881631
15891632 return -1 ;
15901633}
1591-
0 commit comments